abc_parser/datatypes/
writer.rs

1//! Turns datatype representations of ABC back into text.
2
3use super::*;
4
5pub trait ToABC {
6    /// Creates a valid ABC text representation of the object.
7    fn to_abc(&self) -> String;
8}
9
10impl<T> ToABC for Option<T>
11where
12    T: ToABC,
13{
14    fn to_abc(&self) -> String {
15        match self {
16            Some(ref h) => h.to_abc(),
17            None => String::new(),
18        }
19    }
20}
21
22impl ToABC for TuneBook {
23    fn to_abc(&self) -> String {
24        let tunes: Vec<String> = self.tunes.iter().map(|tune| tune.to_abc()).collect();
25        format!("{}\n{}\n", self.header.to_abc(), tunes.join("\n"))
26    }
27}
28
29impl ToABC for FileHeader {
30    fn to_abc(&self) -> String {
31        let s = String::new();
32        let s = self
33            .info
34            .iter()
35            .fold(s, |s, field| format!("{}{}\n", s, field.to_abc()));
36        s
37    }
38}
39
40impl ToABC for InfoField {
41    fn to_abc(&self) -> String {
42        format!("{}:{}", self.0, self.1)
43    }
44}
45
46impl ToABC for Tune {
47    fn to_abc(&self) -> String {
48        format!("{}{}", self.header.to_abc(), self.body.to_abc())
49    }
50}
51
52impl ToABC for TuneHeader {
53    fn to_abc(&self) -> String {
54        let s = self.info.iter().fold(String::new(), |s, field| {
55            format!("{}{}\n", s, field.to_abc())
56        });
57        s
58    }
59}
60
61impl ToABC for TuneBody {
62    fn to_abc(&self) -> String {
63        let lines: Vec<String> = self.music.iter().map(|line| line.to_abc()).collect();
64        lines.join("\n")
65    }
66}
67
68impl ToABC for MusicLine {
69    fn to_abc(&self) -> String {
70        let mut s = String::new();
71        self.symbols
72            .iter()
73            .map(|symbol| s.push_str(&symbol.to_abc()))
74            .count();
75        s
76    }
77}
78
79impl ToABC for MusicSymbol {
80    fn to_abc(&self) -> String {
81        use super::MusicSymbol::*;
82        match self {
83            VisualBreak => String::from(" "),
84            Note {
85                decorations,
86                accidental,
87                note,
88                octave,
89                length,
90                tie,
91            } => {
92                let (note, octave) = denormalise_octave(*note, *octave);
93                format!(
94                    "{}{}{}{}{}{}",
95                    decorations.to_abc(),
96                    accidental.to_abc(),
97                    note,
98                    octave_to_abc(octave),
99                    length_to_abc(*length),
100                    tie.to_abc()
101                )
102            }
103            Bar(bar) => bar.clone(),
104            Rest(rest) => rest.to_abc(),
105            Ending(n) => format!("[{}", n),
106            _ => panic!("Unimplemented for: {:?}", self),
107        }
108    }
109}
110
111impl ToABC for Tie {
112    fn to_abc(&self) -> String {
113        match self {
114            Tie::Solid => "-",
115            Tie::Dotted => ".-",
116        }
117        .to_string()
118    }
119}
120
121/// Given a note and an octave, returns the uppercase or lowercase form of the note which makes the
122/// octave closest to 1, and the octave adjusted accordingly.
123fn denormalise_octave(note: Note, octave: i8) -> (char, i8) {
124    let note_char: char = note.into();
125    if octave > 1 {
126        (note_char.to_ascii_lowercase(), octave - 1)
127    } else {
128        (note_char, octave)
129    }
130}
131
132impl ToABC for Decoration {
133    fn to_abc(&self) -> String {
134        use super::Decoration::*;
135        match self {
136            Staccato => String::from("."),
137            Roll => String::from("~"),
138            Fermata => String::from("H"),
139            Accent => String::from("L"),
140            LowerMordent => String::from("M"),
141            Coda => String::from("O"),
142            UpperMordent => String::from("P"),
143            Segno => String::from("S"),
144            Trill => String::from("T"),
145            UpBow => String::from("u"),
146            DownBow => String::from("v"),
147            Unresolved(s) => format!("!{}!", s),
148        }
149    }
150}
151
152impl ToABC for Vec<Decoration> {
153    fn to_abc(&self) -> String {
154        self.iter().map(ToABC::to_abc).collect()
155    }
156}
157
158impl ToABC for Accidental {
159    fn to_abc(&self) -> String {
160        use super::Accidental::*;
161        String::from(match self {
162            Natural => "=",
163            Sharp => "^",
164            Flat => "_",
165            DoubleSharp => "^^",
166            DoubleFlat => "__",
167        })
168    }
169}
170
171fn octave_to_abc(octave: i8) -> String {
172    match octave {
173        1 => String::new(),
174        o if o > 1 => "'".repeat(octave as usize),
175        o if o < 1 => ",".repeat((-octave + 1) as usize),
176        _ => unreachable!("All patterns covered! How did we get here?"),
177    }
178}
179
180fn length_to_abc(length: f32) -> String {
181    match length {
182        l if l == 1f32 => String::new(),
183        l if l > 1f32 => (length as usize).to_string(),
184        l if l < 1f32 && l > 0f32 => format!("/{}", l.log2() as usize),
185        _ => panic!("Note lengths can't be negative!"),
186    }
187}
188
189impl ToABC for Rest {
190    fn to_abc(&self) -> String {
191        use super::Rest::*;
192
193        match self {
194            Note(n) => format!("z{}", length_to_abc(*n as f32)),
195            Measure(n) => format!("Z{}", length_to_abc(*n as f32)),
196            NoteHidden(n) => format!("x{}", length_to_abc(*n as f32)),
197            MeasureHidden(n) => format!("X{}", length_to_abc(*n as f32)),
198        }
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn info_field() {
208        let f = InfoField('X', "1".to_string());
209        let s = f.to_abc();
210        assert_eq!(s, "X:1")
211    }
212
213    #[test]
214    fn file_header() {
215        let h = FileHeader {
216            info: vec![
217                InfoField('X', "1".to_string()),
218                InfoField('T', "Untitled".to_string()),
219                InfoField('K', "G".to_string()),
220            ],
221        };
222        let s = h.to_abc();
223        assert_eq!(s, "X:1\nT:Untitled\nK:G\n")
224    }
225
226    #[test]
227    fn accidental_1() {
228        assert_eq!(Accidental::Natural.to_abc(), "=")
229    }
230    #[test]
231    fn accidental_2() {
232        assert_eq!(Accidental::Sharp.to_abc(), "^")
233    }
234    #[test]
235    fn accidental_3() {
236        assert_eq!(Accidental::Flat.to_abc(), "_")
237    }
238    #[test]
239    fn accidental_4() {
240        assert_eq!(Accidental::DoubleSharp.to_abc(), "^^")
241    }
242    #[test]
243    fn accidental_5() {
244        assert_eq!(Accidental::DoubleFlat.to_abc(), "__")
245    }
246
247    #[test]
248    fn note_with_accidental() {
249        assert_eq!(
250            MusicSymbol::new_note(vec![], Some(Accidental::Sharp), Note::C, 1, 1.0, None).to_abc(),
251            "^C"
252        );
253    }
254
255    #[test]
256    fn decoration_1() {
257        assert_eq!(Decoration::Staccato.to_abc(), ".")
258    }
259    #[test]
260    fn decoration_2() {
261        assert_eq!(Decoration::Roll.to_abc(), "~")
262    }
263    #[test]
264    fn decoration_3() {
265        assert_eq!(Decoration::Fermata.to_abc(), "H")
266    }
267    #[test]
268    fn decoration_4() {
269        assert_eq!(Decoration::Accent.to_abc(), "L")
270    }
271    #[test]
272    fn decoration_5() {
273        assert_eq!(Decoration::LowerMordent.to_abc(), "M")
274    }
275    #[test]
276    fn decoration_6() {
277        assert_eq!(Decoration::Coda.to_abc(), "O")
278    }
279    #[test]
280    fn decoration_7() {
281        assert_eq!(Decoration::UpperMordent.to_abc(), "P")
282    }
283    #[test]
284    fn decoration_8() {
285        assert_eq!(Decoration::Segno.to_abc(), "S")
286    }
287    #[test]
288    fn decoration_9() {
289        assert_eq!(Decoration::Trill.to_abc(), "T")
290    }
291    #[test]
292    fn decoration_10() {
293        assert_eq!(Decoration::UpBow.to_abc(), "u")
294    }
295    #[test]
296    fn decoration_11() {
297        assert_eq!(Decoration::DownBow.to_abc(), "v")
298    }
299    #[test]
300    fn decoration_12() {
301        assert_eq!(
302            Decoration::Unresolved("asdf".to_string()).to_abc(),
303            "!asdf!"
304        )
305    }
306
307    #[test]
308    fn rest_1() {
309        assert_eq!(Rest::Note(1).to_abc(), "z")
310    }
311    #[test]
312    fn rest_2() {
313        assert_eq!(Rest::Note(2).to_abc(), "z2")
314    }
315    #[test]
316    fn rest_3() {
317        assert_eq!(Rest::Measure(1).to_abc(), "Z")
318    }
319    #[test]
320    fn rest_4() {
321        assert_eq!(Rest::Measure(2).to_abc(), "Z2")
322    }
323    #[test]
324    fn rest_5() {
325        assert_eq!(Rest::NoteHidden(1).to_abc(), "x")
326    }
327    #[test]
328    fn rest_6() {
329        assert_eq!(Rest::NoteHidden(4).to_abc(), "x4")
330    }
331    #[test]
332    fn rest_7() {
333        assert_eq!(Rest::MeasureHidden(1).to_abc(), "X")
334    }
335    #[test]
336    fn rest_8() {
337        assert_eq!(Rest::MeasureHidden(3).to_abc(), "X3")
338    }
339
340    #[test]
341    fn ending_1() {
342        assert_eq!(MusicSymbol::Ending(1).to_abc(), "[1")
343    }
344    #[test]
345    fn ending_2() {
346        assert_eq!(MusicSymbol::Ending(2).to_abc(), "[2")
347    }
348
349    #[test]
350    fn tie() {
351        assert_eq!(
352            MusicSymbol::new_note(vec![], None, Note::C, 1, 1.0, Some(Tie::Solid)).to_abc(),
353            "C-"
354        );
355    }
356}