chordlib 0.15.0

Work with chord-and-lyrics songs: parse, transform, and render them to multiple formats
Documentation
use super::{FormatOutputLines, OutputLine};
use crate::Error;
use crate::types::{ChordRepresentation, SimpleChord, Song};

pub trait FormatRender {
    fn format_render(
        &self,
        key: Option<&SimpleChord>,
        representation: Option<&ChordRepresentation>,
        language: Option<usize>,
    ) -> Result<String, Error>;
}

impl FormatRender for Song {
    fn format_render(
        &self,
        key: Option<&SimpleChord>,
        representation: Option<&ChordRepresentation>,
        language: Option<usize>,
    ) -> Result<String, Error> {
        let lines = self.format_output_lines(key, representation, language)?;
        Ok(lines
            .iter()
            .map(|line| match line {
                OutputLine::Keyword(keyword) => format!("\x1b[31;1m{}\x1b[0m", keyword),
                OutputLine::Chord(chord) => format!("\x1b[32;1m{}\x1b[0m", chord),
                OutputLine::Text(text) => format!("\x1b[32m{}\x1b[0m", text),
            })
            .collect::<Vec<String>>()
            .join("\n"))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::inputs::chord_pro::load_string;
    use crate::types::SongFlowItem;

    fn flow_item(title: &str, occurrence_index: u32, repeats: u32) -> SongFlowItem {
        SongFlowItem {
            title: title.to_string(),
            occurrence_index,
            repeats,
        }
    }

    #[test]
    fn format_render_supports_custom_flow_after_apply_flow() {
        let input = r#"{title: Ohne Titel}
{key: A}
{section: Tag 1}
Text 1
{section: Tag 2}
Text 2
{section: Tag 3}
Text 3
{section: Tag 1}
{section: Tag 2}
{section: Tag 3}
"#;
        let mut song = load_string(input).expect("parse");
        let rep = ChordRepresentation::Default;
        let flow = vec![
            flow_item("Tag 1", 0, 1),
            flow_item("Tag 2", 0, 1),
            flow_item("Tag 3", 0, 1),
            flow_item("Tag 1", 0, 2),
            flow_item("Tag 3", 0, 1),
            flow_item("Tag 2", 0, 1),
        ];

        song.apply_flow(flow).expect("apply flow");

        let custom_render = song.format_render(None, Some(&rep), None).expect("render");
        assert!(custom_render.contains("\x1b[31;1mTag 1\x1b[0m"));
        assert!(custom_render.contains("\x1b[32mText 1\x1b[0m"));
        assert!(custom_render.contains("\x1b[32m(repeat)\x1b[0m"));
    }
}