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
use crate::time::{beat::Beat, measure::Measure, time_signature::TimeSignature};
use core::ops::{Add, AddAssign, Mul, Sub, SubAssign};

#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct CompoundDuration {
    pub measure: Measure,
    pub beat: Beat,
}

macro_rules! compound_conversion {
    ($ty:ident, $field:ident, $other_ty:ident, $other_field:ident) => {
        impl From<$ty> for CompoundDuration {
            fn from($field: $ty) -> Self {
                Self {
                    $other_field: Default::default(),
                    $field,
                }
            }
        }

        impl Add<$ty> for CompoundDuration {
            type Output = CompoundDuration;

            fn add(self, $field: $ty) -> Self::Output {
                CompoundDuration {
                    $field: self.$field + $field,
                    $other_field: self.$other_field,
                }
            }
        }

        impl AddAssign<$ty> for CompoundDuration {
            fn add_assign(&mut self, $field: $ty) {
                self.$field += $field;
            }
        }

        impl Sub<$ty> for CompoundDuration {
            type Output = CompoundDuration;

            fn sub(self, $field: $ty) -> Self::Output {
                CompoundDuration {
                    $field: self.$field - $field,
                    $other_field: self.$other_field,
                }
            }
        }

        impl SubAssign<$ty> for CompoundDuration {
            fn sub_assign(&mut self, $field: $ty) {
                self.$field -= $field;
            }
        }

        impl Add<$ty> for $other_ty {
            type Output = CompoundDuration;

            fn add(self, value: $ty) -> Self::Output {
                let compound: CompoundDuration = self.into();
                compound + value
            }
        }

        impl Sub<$ty> for $other_ty {
            type Output = CompoundDuration;

            fn sub(self, value: $ty) -> Self::Output {
                let compound: CompoundDuration = self.into();
                compound - value
            }
        }
    };
}

compound_conversion!(Beat, beat, Measure, measure);
compound_conversion!(Measure, measure, Beat, beat);

impl Mul<TimeSignature> for CompoundDuration {
    type Output = Beat;

    fn mul(self, time_signature: TimeSignature) -> Self::Output {
        self.beat * time_signature + self.measure * time_signature
    }
}