1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::structs::*;

error_chain! {
    errors {
        #[doc="the path encoding is invalid"]
        InvalidPathEncoding(tag: &'static str) {
            description("invalid path encoding")
            display("invalid path encoding on tag: {}", tag)
        }
    }
}

/// Converts a Song back to the Ultrastar Song format and returns it as a String
///
/// # Arguments
/// * header - the Header struct of the song
/// * lines - a vector of the songs lines
///
pub fn generate_song_txt(header: &Header, lines: &[Line]) -> Result<String> {
    // generate header
    let mp3_str = match header.audio_path.to_str() {
        Some(x) => x,
        None => bail!(ErrorKind::InvalidPathEncoding("MP3")),
    };
    let mut song_txt_str = format!(
        "#TITLE:{}\n#ARTIST:{}\n#MP3:{}\n#BPM:{}\n",
        header.title, header.artist, mp3_str, header.bpm
    );
    if let Some(gap) = header.gap {
        song_txt_str.push_str(&format!("#GAP:{}\n", gap));
    }
    if let Some(cover_path) = header.cover_path.clone() {
        let cover_str = match cover_path.to_str() {
            Some(x) => x,
            None => bail!(ErrorKind::InvalidPathEncoding("COVER")),
        };
        song_txt_str.push_str(&format!("#COVER:{}\n", cover_str));
    }
    if let Some(background_path) = header.background_path.clone() {
        let background_str = match background_path.to_str() {
            Some(x) => x,
            None => bail!(ErrorKind::InvalidPathEncoding("BACKGROUND")),
        };
        song_txt_str.push_str(&format!("#BACKGROUND:{}\n", background_str));
    }
    if let Some(video_path) = header.video_path.clone() {
        let video_str = match video_path.to_str() {
            Some(x) => x,
            None => bail!(ErrorKind::InvalidPathEncoding("VIDEO")),
        };
        song_txt_str.push_str(&format!("#VIDEO:{}\n", video_str));
    }
    if let Some(videogap) = header.video_gap {
        song_txt_str.push_str(&format!("#VIDEOGAP:{}\n", videogap));
    }
    if let Some(genre) = header.genre.clone() {
        song_txt_str.push_str(&format!("#GENRE:{}\n", genre));
    }
    if let Some(edition) = header.edition.clone() {
        song_txt_str.push_str(&format!("#EDITION:{}\n", edition));
    }
    if let Some(language) = header.language.clone() {
        song_txt_str.push_str(&format!("#LANGUAGE:{}\n", language));
    }
    if let Some(year) = header.year {
        song_txt_str.push_str(&format!("#YEAR:{}\n", year));
    }
    if let Some(relative) = header.relative {
        if relative {
            song_txt_str.push_str("#RELATIVE:YES\n");
        } else {
            song_txt_str.push_str("#RELATIVE:NO\n");
        }
    }
    if let Some(unknown) = header.unknown.clone() {
        for (key, value) in unknown.iter() {
            song_txt_str.push_str(&format!("#{}:{}\n", key, value));
        }
    }

    // generate lines
    for line in lines.iter() {
        if line.start != 0 {
            if line.rel.is_some() {
                song_txt_str.push_str(format!("- {} {}\n", line.start, line.rel.unwrap()).as_ref());
            } else {
                song_txt_str.push_str(format!("- {}\n", line.start).as_ref());
            }
        }
        for note in line.notes.iter() {
            match *note {
                Note::Regular {
                    start,
                    duration,
                    pitch,
                    ref text,
                } => song_txt_str
                    .push_str(format!(": {} {} {} {}\n", start, duration, pitch, text).as_ref()),
                Note::Golden {
                    start,
                    duration,
                    pitch,
                    ref text,
                } => song_txt_str
                    .push_str(format!("* {} {} {} {}\n", start, duration, pitch, text).as_ref()),
                Note::Freestyle {
                    start,
                    duration,
                    pitch,
                    ref text,
                } => song_txt_str
                    .push_str(format!("F {} {} {} {}\n", start, duration, pitch, text).as_ref()),
                Note::PlayerChange { player } => {
                    song_txt_str.push_str(format!("P{}\n", player).as_ref())
                }
            };
        }
    }
    song_txt_str.push_str("E");
    Ok(song_txt_str)
}