1use super::*;
4
5pub trait ToABC {
6 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result;
11
12 fn write_abc(&self, mut writer: impl std::fmt::Write) -> std::fmt::Result {
13 self.write_mut_abc(&mut writer)
14 }
15
16 fn to_abc(&self) -> String {
18 let mut s = String::new();
19 self.write_abc(&mut s).unwrap();
20 s
21 }
22}
23
24impl<T: ToABC> ToABC for Option<T> {
25 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
26 match self {
27 Some(h) => h.write_mut_abc(writer),
28 None => Ok(()),
29 }
30 }
31}
32
33impl<T: ToABC> ToABC for Vec<T> {
34 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
35 self.iter().try_for_each(|i| i.write_mut_abc(writer))
36 }
37}
38
39impl ToABC for Length {
40 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
41 #[allow(clippy::redundant_guards)]
42 match self.0 {
43 l if l < 1.0 && l > 0.0 => {
44 let inv = 1.0 / l;
45 let num_halves = inv.log2();
46 if num_halves.fract() == 0.0 {
47 (0..(num_halves as u64)).try_for_each(|_| writer.write_str("/"))
48 } else {
49 write!(writer, "/{}", inv as usize)
50 }
51 }
52 l if l >= 0.0 => write!(writer, "{}", fraction::Fraction::from(l)),
53 _ => panic!("Note lengths must be greater than 0!"),
54 }
55 }
56}
57
58impl ToABC for TuneBook {
59 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
60 self.comments.iter().try_for_each(|c| {
61 c.write_mut_abc(writer)?;
62 writer.write_str("\n")
63 })?;
64
65 if let Some(ref header) = self.header {
66 header.write_mut_abc(writer)?;
67 writer.write_str("\n")?;
68 }
69
70 self.tunes.iter().try_for_each(|t| {
71 t.write_mut_abc(writer)?;
72 writer.write_str("\n")
73 })
74 }
75}
76
77impl ToABC for IgnoredLine {
78 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
79 match self {
80 IgnoredLine::Comment(comment) => comment.write_mut_abc(writer),
81 IgnoredLine::EmptyLine => Ok(()),
82 }
83 }
84}
85
86impl ToABC for FileHeader {
87 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
88 join_header_lines(&self.lines, writer)
89 }
90}
91
92impl ToABC for InfoField {
93 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
94 write!(writer, "{}:{}", self.0, self.1)
95 }
96}
97
98impl ToABC for Tune {
99 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
100 self.header.write_mut_abc(writer)?;
101 self.body.write_mut_abc(writer)
102 }
103}
104
105impl ToABC for TuneHeader {
106 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
107 join_header_lines(&self.lines, writer)
108 }
109}
110
111impl ToABC for HeaderLine {
112 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
113 match self {
114 HeaderLine::Field(field, comment) => {
115 field.write_mut_abc(writer)?;
116 comment.write_mut_abc(writer)
117 }
118 HeaderLine::Comment(comment) => comment.write_mut_abc(writer),
119 }
120 }
121}
122
123fn join_header_lines(lines: &[HeaderLine], writer: &mut impl std::fmt::Write) -> std::fmt::Result {
124 for line in lines {
125 line.write_mut_abc(writer)?;
126 writer.write_str("\n")?;
127 }
128
129 Ok(())
130}
131
132impl ToABC for TuneBody {
133 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
134 for (i, line) in self.lines.iter().enumerate() {
135 line.write_mut_abc(writer)?;
136 if i + 1 != self.lines.len() {
137 writer.write_str("\n")?;
138 }
139 }
140
141 Ok(())
142 }
143}
144
145impl ToABC for TuneLine {
146 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
147 match self {
148 TuneLine::Comment(comment) => comment.write_mut_abc(writer),
149 TuneLine::Music(line) => line.write_mut_abc(writer),
150 TuneLine::Symbol(line) => line.write_mut_abc(writer),
151 TuneLine::Lyric(line) => line.write_mut_abc(writer),
152 }
153 }
154}
155
156impl ToABC for MusicLine {
157 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
158 self.symbols
159 .iter()
160 .try_for_each(|symbol| symbol.write_mut_abc(writer))
161 }
162}
163
164impl ToABC for SymbolLine {
165 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
166 writer.write_str("s:")?;
167 self.symbols
168 .iter()
169 .try_for_each(|symbol| symbol.write_mut_abc(writer))
170 }
171}
172
173impl ToABC for LyricLine {
174 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
175 writer.write_str("w:")?;
176 self.symbols
177 .iter()
178 .try_for_each(|symbol| symbol.write_mut_abc(writer))
179 }
180}
181
182impl ToABC for MusicSymbol {
183 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
184 use super::MusicSymbol::*;
185 match self {
186 Note {
187 accidental,
188 note,
189 octave,
190 length,
191 tie,
192 } => {
193 let (note, octave) = denormalise_octave(*note, *octave);
194 accidental.write_mut_abc(writer)?;
195 writer.write_char(note)?;
196 write_octave(octave, writer)?;
197 length.write_mut_abc(writer)?;
198 tie.write_mut_abc(writer)
199 }
200 GraceNotes {
201 acciaccatura,
202 notes,
203 } => {
204 write!(writer, "{{{}", acciaccatura.map_or("", |_| "/"))?;
205 notes.write_mut_abc(writer)?;
206 writer.write_str("}")
207 }
208 Chord { notes, length, tie } => {
209 writer.write_str("[")?;
210 notes.write_mut_abc(writer)?;
211 writer.write_str("]")?;
212 length.write_mut_abc(writer)?;
213 tie.write_mut_abc(writer)
214 }
215 Tuplet { p, q, r } => match (q, r) {
216 (None, None) => write!(writer, "({}", p),
217 (Some(q), None) => write!(writer, "({}:{}", p, q),
218 (None, Some(r)) => write!(writer, "({}::{}", p, r),
219 (Some(q), Some(r)) => write!(writer, "({}:{}:{}", p, q, r),
220 },
221 BrokenRhythm {
222 rhythm,
223 before,
224 after,
225 } => {
226 before.write_mut_abc(writer)?;
227 writer.write_str(rhythm)?;
228 after.write_mut_abc(writer)
229 }
230 Decoration(decoration) => decoration.write_mut_abc(writer),
231 Annotation(annotation) => annotation.write_mut_abc(writer),
232 Bar(bar, ending) => {
233 writer.write_str(bar)?;
234 if let Some(ending) = ending {
235 writer.write_str(ending)?;
236 }
237 Ok(())
238 }
239 Beam(beam) => writer.write_str(beam),
240 Slur(slur) => slur.write_mut_abc(writer),
241 Comment(comment) => comment.write_mut_abc(writer),
242 Rest(rest) => rest.write_mut_abc(writer),
243 Spacer => writer.write_str("y"),
244 Ending(n) => write!(writer, "[{}", n),
245 InlineField(info_field, bracketed) => {
246 if *bracketed {
247 writer.write_str("[")?;
248 }
249 info_field.write_mut_abc(writer)?;
250 if *bracketed {
251 writer.write_str("]")?;
252 }
253 Ok(())
254 }
255 Space(s) => writer.write_str(s),
256 Reserved(s) => writer.write_str(s),
257 }
258 }
259}
260
261impl ToABC for SymbolLineSymbol {
262 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
263 use super::SymbolLineSymbol::*;
264 match self {
265 Annotation(annotation) => annotation.write_mut_abc(writer),
266 Decoration(decoration) => decoration.write_mut_abc(writer),
267 SymbolAlignment(alignment) => alignment.write_mut_abc(writer),
268 Space(s) => writer.write_str(s),
269 }
270 }
271}
272
273impl ToABC for LyricSymbol {
274 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
275 use super::LyricSymbol::*;
276 match self {
277 Syllable(syllable) => writer.write_str(syllable),
278 SymbolAlignment(alignment) => alignment.write_mut_abc(writer),
279 Space(s) => writer.write_str(s),
280 }
281 }
282}
283
284impl ToABC for Annotation {
285 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
286 writer.write_char('"')?;
287 self.placement.write_mut_abc(writer)?;
288 write!(writer, "{}\"", self.text)
289 }
290}
291
292impl ToABC for Placement {
293 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
294 use super::Placement::*;
295 writer.write_str(match self {
296 Above => "^",
297 Below => "_",
298 Left => "<",
299 Right => ">",
300 Auto => "@",
301 })
302 }
303}
304
305impl ToABC for Tie {
306 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
307 use super::Tie::*;
308 writer.write_str(match self {
309 Solid => "-",
310 Dotted => ".-",
311 })
312 }
313}
314
315impl ToABC for Slur {
316 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
317 use super::Slur::*;
318 writer.write_str(match self {
319 Begin => "(",
320 BeginDotted => ".(",
321 End => ")",
322 })
323 }
324}
325
326fn denormalise_octave(note: Note, octave: i8) -> (char, i8) {
329 let note_char: char = note.into();
330 if octave > 1 {
331 (note_char.to_ascii_lowercase(), octave - 1)
332 } else {
333 (note_char, octave)
334 }
335}
336
337impl ToABC for Decoration {
338 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
339 use super::Decoration::*;
340 match self {
341 Staccato => writer.write_str("."),
342 Roll => writer.write_str("~"),
343 Fermata => writer.write_str("H"),
344 Accent => writer.write_str("L"),
345 LowerMordent => writer.write_str("M"),
346 Coda => writer.write_str("O"),
347 UpperMordent => writer.write_str("P"),
348 Segno => writer.write_str("S"),
349 Trill => writer.write_str("T"),
350 UpBow => writer.write_str("u"),
351 DownBow => writer.write_str("v"),
352 Unresolved(s) => write!(writer, "!{}!", s),
353 }
354 }
355}
356
357impl ToABC for Accidental {
358 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
359 use super::Accidental::*;
360 writer.write_str(match self {
361 Natural => "=",
362 Sharp => "^",
363 Flat => "_",
364 DoubleSharp => "^^",
365 DoubleFlat => "__",
366 })
367 }
368}
369
370impl ToABC for SymbolAlignment {
371 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
372 use super::SymbolAlignment::*;
373 writer.write_str(match self {
374 Break => "-",
375 Extend => "_",
376 Skip => "*",
377 Space => "~",
378 Hyphen => "\\-",
379 Bar => "|",
380 })
381 }
382}
383
384fn write_octave(octave: i8, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
385 match octave {
386 o if o > 1 => (0..(octave - 1) as usize).try_for_each(|_| write!(writer, "'")),
387 o if o < 1 => (0..(-octave + 1) as usize).try_for_each(|_| write!(writer, ",")),
388 _ => Ok(()),
389 }
390}
391
392impl ToABC for Rest {
393 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
394 use super::Rest::*;
395 match self {
396 Note(l) => {
397 writer.write_str("z")?;
398 l.write_mut_abc(writer)
399 }
400 Measure(l) => {
401 writer.write_str("Z")?;
402 l.write_mut_abc(writer)
403 }
404 NoteHidden(l) => {
405 writer.write_str("x")?;
406 l.write_mut_abc(writer)
407 }
408 MeasureHidden(l) => {
409 writer.write_str("X")?;
410 l.write_mut_abc(writer)
411 }
412 }
413 }
414}
415
416impl ToABC for Comment {
417 fn write_mut_abc(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
418 use super::Comment::*;
419 match self {
420 Comment(comment) => write!(writer, "%{comment}"),
421 CommentLine(space, comment) => write!(writer, "{space}%{comment}"),
422 StylesheetDirective(directive) => write!(writer, "%%{directive}"),
423 }
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use pretty_assertions::assert_eq;
430
431 use super::*;
432 use crate::datatypes::builder::NoteBuilder;
433
434 #[test]
435 fn info_field() {
436 let f = InfoField('X', "1".to_string());
437 let s = f.to_abc();
438 assert_eq!(s, "X:1")
439 }
440
441 #[test]
442 fn file_header() {
443 let h = FileHeader {
444 lines: vec![
445 HeaderLine::Field(InfoField('X', "1".to_string()), None),
446 HeaderLine::Field(InfoField('T', "Untitled".to_string()), None),
447 HeaderLine::Field(InfoField('K', "G".to_string()), None),
448 ],
449 };
450 let s = h.to_abc();
451 assert_eq!(s, "X:1\nT:Untitled\nK:G\n")
452 }
453
454 #[test]
455 fn file_header_with_comments() {
456 let h = FileHeader {
457 lines: vec![
458 HeaderLine::Field(InfoField('X', "1".to_string()), None),
459 HeaderLine::Field(
460 InfoField('T', "Untitled ".to_string()),
461 Some(Comment::Comment(" Inline comment".to_string())),
462 ),
463 HeaderLine::Field(InfoField('K', "G".to_string()), None),
464 ],
465 };
466 let s = h.to_abc();
467 assert_eq!(s, "X:1\nT:Untitled % Inline comment\nK:G\n")
468 }
469
470 #[test]
471 fn file_header_with_stylesheet_directive() {
472 let h = FileHeader {
473 lines: vec![
474 HeaderLine::Field(InfoField('X', "1".to_string()), None),
475 HeaderLine::Field(InfoField('T', "Untitled".to_string()), None),
476 HeaderLine::Comment(Comment::StylesheetDirective("papersize A4".to_string())),
477 HeaderLine::Field(InfoField('K', "G".to_string()), None),
478 ],
479 };
480 let s = h.to_abc();
481 assert_eq!(s, "X:1\nT:Untitled\n%%papersize A4\nK:G\n")
482 }
483
484 #[test]
485 fn accidental_1() {
486 assert_eq!(Accidental::Natural.to_abc(), "=")
487 }
488 #[test]
489 fn accidental_2() {
490 assert_eq!(Accidental::Sharp.to_abc(), "^")
491 }
492 #[test]
493 fn accidental_3() {
494 assert_eq!(Accidental::Flat.to_abc(), "_")
495 }
496 #[test]
497 fn accidental_4() {
498 assert_eq!(Accidental::DoubleSharp.to_abc(), "^^")
499 }
500 #[test]
501 fn accidental_5() {
502 assert_eq!(Accidental::DoubleFlat.to_abc(), "__")
503 }
504
505 #[test]
506 fn note_with_accidental() {
507 assert_eq!(
508 NoteBuilder::new(Note::C)
509 .accidental(Accidental::Sharp)
510 .build()
511 .to_abc(),
512 "^C"
513 );
514 }
515
516 #[test]
517 fn decoration_1() {
518 assert_eq!(Decoration::Staccato.to_abc(), ".")
519 }
520 #[test]
521 fn decoration_2() {
522 assert_eq!(Decoration::Roll.to_abc(), "~")
523 }
524 #[test]
525 fn decoration_3() {
526 assert_eq!(Decoration::Fermata.to_abc(), "H")
527 }
528 #[test]
529 fn decoration_4() {
530 assert_eq!(Decoration::Accent.to_abc(), "L")
531 }
532 #[test]
533 fn decoration_5() {
534 assert_eq!(Decoration::LowerMordent.to_abc(), "M")
535 }
536 #[test]
537 fn decoration_6() {
538 assert_eq!(Decoration::Coda.to_abc(), "O")
539 }
540 #[test]
541 fn decoration_7() {
542 assert_eq!(Decoration::UpperMordent.to_abc(), "P")
543 }
544 #[test]
545 fn decoration_8() {
546 assert_eq!(Decoration::Segno.to_abc(), "S")
547 }
548 #[test]
549 fn decoration_9() {
550 assert_eq!(Decoration::Trill.to_abc(), "T")
551 }
552 #[test]
553 fn decoration_10() {
554 assert_eq!(Decoration::UpBow.to_abc(), "u")
555 }
556 #[test]
557 fn decoration_11() {
558 assert_eq!(Decoration::DownBow.to_abc(), "v")
559 }
560 #[test]
561 fn decoration_12() {
562 assert_eq!(
563 Decoration::Unresolved("asdf".to_string()).to_abc(),
564 "!asdf!"
565 )
566 }
567
568 #[test]
569 fn rest_1() {
570 assert_eq!(Rest::Note(None).to_abc(), "z")
571 }
572 #[test]
573 fn rest_2() {
574 assert_eq!(Rest::Note(Some(Length::new(2.0))).to_abc(), "z2")
575 }
576 #[test]
577 fn rest_3() {
578 assert_eq!(Rest::Measure(None).to_abc(), "Z")
579 }
580 #[test]
581 fn rest_4() {
582 assert_eq!(Rest::Measure(Some(Length::new(2.0))).to_abc(), "Z2")
583 }
584 #[test]
585 fn rest_5() {
586 assert_eq!(Rest::NoteHidden(None).to_abc(), "x")
587 }
588 #[test]
589 fn rest_6() {
590 assert_eq!(Rest::NoteHidden(Some(Length::new(4.0))).to_abc(), "x4")
591 }
592 #[test]
593 fn rest_7() {
594 assert_eq!(Rest::MeasureHidden(None).to_abc(), "X")
595 }
596 #[test]
597 fn rest_8() {
598 assert_eq!(Rest::MeasureHidden(Some(Length::new(3.0))).to_abc(), "X3")
599 }
600
601 #[test]
602 fn spacer() {
603 assert_eq!(MusicSymbol::Spacer.to_abc(), "y")
604 }
605
606 #[test]
607 fn ending_1() {
608 assert_eq!(MusicSymbol::Ending("1".to_string()).to_abc(), "[1")
609 }
610 #[test]
611 fn ending_2() {
612 assert_eq!(MusicSymbol::Ending("2".to_string()).to_abc(), "[2")
613 }
614 #[test]
615 fn ending_3() {
616 assert_eq!(
617 MusicSymbol::Ending("1,3,5-7,8-10".to_string()).to_abc(),
618 "[1,3,5-7,8-10"
619 )
620 }
621
622 #[test]
623 fn bar_ending() {
624 assert_eq!(
625 MusicSymbol::Bar("|".to_string(), Some("2".to_string())).to_abc(),
626 "|2"
627 )
628 }
629
630 #[test]
631 fn octave_1() {
632 assert_eq!(NoteBuilder::new(Note::C).octave(2).build().to_abc(), "c");
633 }
634 #[test]
635 fn octave_2() {
636 assert_eq!(NoteBuilder::new(Note::C).octave(3).build().to_abc(), "c'");
637 }
638 #[test]
639 fn octave_3() {
640 assert_eq!(NoteBuilder::new(Note::C).octave(4).build().to_abc(), "c''");
641 }
642 #[test]
643 fn octave_4() {
644 assert_eq!(NoteBuilder::new(Note::C).octave(0).build().to_abc(), "C,");
645 }
646 #[test]
647 fn octave_5() {
648 assert_eq!(NoteBuilder::new(Note::C).octave(-1).build().to_abc(), "C,,");
649 }
650
651 #[test]
652 fn length_1() {
653 assert_eq!(NoteBuilder::new(Note::C).build().to_abc(), "C");
654 }
655 #[test]
656 fn length_2() {
657 assert_eq!(NoteBuilder::new(Note::C).length(2.0).build().to_abc(), "C2");
658 }
659 #[test]
660 fn length_3() {
661 assert_eq!(NoteBuilder::new(Note::C).length(0.5).build().to_abc(), "C/");
662 }
663 #[test]
664 fn length_4() {
665 assert_eq!(NoteBuilder::new(Note::C).length(1.0).build().to_abc(), "C1");
666 }
667 #[test]
668 fn length_5() {
669 assert_eq!(
670 NoteBuilder::new(Note::C).length(1.5).build().to_abc(),
671 "C3/2"
672 );
673 }
674 #[test]
675 fn length_6() {
676 assert_eq!(
677 NoteBuilder::new(Note::C).length(0.125).build().to_abc(),
678 "C///"
679 );
680 }
681 #[test]
682 fn length_7() {
683 assert_eq!(
684 NoteBuilder::new(Note::C).length(1.0 / 3.0).build().to_abc(),
685 "C/3"
686 );
687 }
688 #[test]
689 fn length_8() {
690 assert_eq!(
691 NoteBuilder::new(Note::C).length(0.2).build().to_abc(),
692 "C/5"
693 );
694 }
695 #[ignore]
696 #[test]
697 fn length_9() {
698 assert_eq!(
699 NoteBuilder::new(Note::C).length(2.0 / 3.0).build().to_abc(),
700 "C2/3"
701 );
702 }
703
704 #[test]
705 fn tie() {
706 assert_eq!(
707 NoteBuilder::new(Note::C).tie(Tie::Solid).build().to_abc(),
708 "C-"
709 );
710 }
711
712 #[test]
713 fn slur_1() {
714 assert_eq!(MusicSymbol::Slur(Slur::Begin).to_abc(), "(");
715 }
716 #[test]
717 fn slur_2() {
718 assert_eq!(MusicSymbol::Slur(Slur::BeginDotted).to_abc(), ".(");
719 }
720 #[test]
721 fn slur_3() {
722 assert_eq!(MusicSymbol::Slur(Slur::End).to_abc(), ")");
723 }
724
725 #[test]
726 fn beam() {
727 assert_eq!(MusicSymbol::Beam("````".to_string()).to_abc(), "````")
728 }
729
730 #[test]
731 fn grace_notes_1() {
732 assert_eq!(
733 MusicSymbol::GraceNotes {
734 acciaccatura: None,
735 notes: vec![
736 NoteBuilder::new(Note::A)
737 .accidental(Accidental::DoubleSharp)
738 .build(),
739 NoteBuilder::new(Note::B).build(),
740 NoteBuilder::new(Note::C)
741 .accidental(Accidental::Flat)
742 .octave(2)
743 .length(2.0)
744 .build(),
745 ]
746 }
747 .to_abc(),
748 "{^^AB_c2}"
749 );
750 }
751
752 #[test]
753 fn grace_notes_2() {
754 assert_eq!(
755 MusicSymbol::GraceNotes {
756 acciaccatura: Some(()),
757 notes: vec![
758 NoteBuilder::new(Note::A)
759 .accidental(Accidental::DoubleSharp)
760 .build()
761 ]
762 }
763 .to_abc(),
764 "{/^^A}"
765 );
766 }
767
768 #[test]
769 fn tuplets_1() {
770 assert_eq!(
771 MusicSymbol::Tuplet {
772 p: 3,
773 q: None,
774 r: None,
775 }
776 .to_abc(),
777 "(3"
778 );
779 }
780
781 #[test]
782 fn tuplets_2() {
783 assert_eq!(
784 MusicSymbol::Tuplet {
785 p: 3,
786 q: Some(2),
787 r: None,
788 }
789 .to_abc(),
790 "(3:2"
791 );
792 }
793
794 #[test]
795 fn tuplets_3() {
796 assert_eq!(
797 MusicSymbol::Tuplet {
798 p: 3,
799 q: None,
800 r: Some(2),
801 }
802 .to_abc(),
803 "(3::2"
804 );
805 }
806
807 #[test]
808 fn broken_rhythm_1() {
809 assert_eq!(
810 MusicSymbol::BrokenRhythm {
811 rhythm: "<".to_string(),
812 before: vec![NoteBuilder::new(Note::A).build()],
813 after: vec![NoteBuilder::new(Note::B).build()]
814 }
815 .to_abc(),
816 "A<B"
817 );
818 }
819
820 #[test]
821 fn broken_rhythm_2() {
822 assert_eq!(
823 MusicSymbol::BrokenRhythm {
824 rhythm: ">".to_string(),
825 before: vec![NoteBuilder::new(Note::A).build()],
826 after: vec![NoteBuilder::new(Note::B).build()]
827 }
828 .to_abc(),
829 "A>B"
830 );
831 }
832
833 #[test]
834 fn broken_rhythm_3() {
835 assert_eq!(
836 MusicSymbol::BrokenRhythm {
837 rhythm: "<<".to_string(),
838 before: vec![NoteBuilder::new(Note::A).build()],
839 after: vec![NoteBuilder::new(Note::B).build()]
840 }
841 .to_abc(),
842 "A<<B"
843 );
844 }
845
846 #[test]
847 fn broken_rhythm_4() {
848 assert_eq!(
849 MusicSymbol::BrokenRhythm {
850 rhythm: ">>>".to_string(),
851 before: vec![NoteBuilder::new(Note::A).build()],
852 after: vec![NoteBuilder::new(Note::B).build()]
853 }
854 .to_abc(),
855 "A>>>B"
856 );
857 }
858
859 #[test]
860 fn broken_rhythm_5() {
861 assert_eq!(
862 MusicSymbol::BrokenRhythm {
863 rhythm: ">".to_string(),
864 before: vec![
865 NoteBuilder::new(Note::A).build(),
866 MusicSymbol::Space(" ".to_string()),
867 ],
868 after: vec![NoteBuilder::new(Note::B).build()]
869 }
870 .to_abc(),
871 "A >B"
872 );
873 }
874
875 #[test]
876 fn chord_1() {
877 assert_eq!(
878 MusicSymbol::Chord {
879 notes: vec![
880 NoteBuilder::new(Note::C).build(),
881 NoteBuilder::new(Note::E).build(),
882 NoteBuilder::new(Note::G).build(),
883 NoteBuilder::new(Note::C).octave(2).build(),
884 ],
885 length: None,
886 tie: None,
887 }
888 .to_abc(),
889 "[CEGc]"
890 );
891 }
892
893 #[test]
894 fn chord_2() {
895 assert_eq!(
896 MusicSymbol::Chord {
897 notes: vec![
898 NoteBuilder::new(Note::C).build(),
899 NoteBuilder::new(Note::E).length(2.0).build(),
900 NoteBuilder::new(Note::G)
901 .accidental(Accidental::Sharp)
902 .build(),
903 NoteBuilder::new(Note::C).octave(2).build(),
904 ],
905 length: Some(Length::new(3.0)),
906 tie: Some(Tie::Solid),
907 }
908 .to_abc(),
909 "[CE2^Gc]3-"
910 );
911 }
912
913 #[test]
914 fn annotation_1() {
915 assert_eq!(
916 Annotation::new(Some(Placement::Above), "foobar".to_string()).to_abc(),
917 "\"^foobar\""
918 );
919 }
920 #[test]
921 fn annotation_2() {
922 assert_eq!(
923 Annotation::new(Some(Placement::Below), "foobar".to_string()).to_abc(),
924 "\"_foobar\""
925 );
926 }
927 #[test]
928 fn annotation_3() {
929 assert_eq!(
930 Annotation::new(Some(Placement::Left), "foobar".to_string()).to_abc(),
931 "\"<foobar\""
932 );
933 }
934 #[test]
935 fn annotation_4() {
936 assert_eq!(
937 Annotation::new(Some(Placement::Right), "foobar".to_string()).to_abc(),
938 "\">foobar\""
939 );
940 }
941 #[test]
942 fn annotation_5() {
943 assert_eq!(
944 Annotation::new(Some(Placement::Auto), "foobar".to_string()).to_abc(),
945 "\"@foobar\""
946 );
947 }
948 #[test]
949 fn annotation_6() {
950 assert_eq!(
951 Annotation::new(None, "foobar".to_string()).to_abc(),
952 "\"foobar\""
953 );
954 }
955}