1use super::*;
4
5pub trait ToABC {
6 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
121fn 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}