use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChangeScope {
Global,
Part(usize),
Measures { part: usize, staff: usize, start: usize, end: usize },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChangeHint {
pub scope: ChangeScope,
pub layout_dirty: bool,
pub playback_dirty: bool,
}
impl ChangeHint {
pub fn merge(self, other: ChangeHint) -> ChangeHint {
ChangeHint {
scope: merge_scope(self.scope, other.scope),
layout_dirty: self.layout_dirty || other.layout_dirty,
playback_dirty: self.playback_dirty || other.playback_dirty,
}
}
}
fn merge_scope(a: ChangeScope, b: ChangeScope) -> ChangeScope {
match (a, b) {
(ChangeScope::Global, _) | (_, ChangeScope::Global) => ChangeScope::Global,
(ChangeScope::Part(pa), ChangeScope::Part(pb)) if pa == pb => ChangeScope::Part(pa),
(ChangeScope::Part(_), ChangeScope::Part(_)) => ChangeScope::Global,
(
ChangeScope::Measures { part: pa, staff: sa, start: s1, end: e1 },
ChangeScope::Measures { part: pb, staff: sb, start: s2, end: e2 },
) if pa == pb && sa == sb => {
ChangeScope::Measures { part: pa, staff: sa, start: s1.min(s2), end: e1.max(e2) }
}
_ => ChangeScope::Global,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hint(scope: ChangeScope, layout: bool, playback: bool) -> ChangeHint {
ChangeHint { scope, layout_dirty: layout, playback_dirty: playback }
}
#[test]
fn merge_global_wins() {
let a = hint(ChangeScope::Global, false, true);
let b = hint(ChangeScope::Part(0), true, false);
let m = a.merge(b);
assert_eq!(m.scope, ChangeScope::Global);
assert!(m.layout_dirty);
assert!(m.playback_dirty);
}
#[test]
fn merge_same_part_stays_part() {
let a = hint(ChangeScope::Part(1), false, true);
let b = hint(ChangeScope::Part(1), true, false);
let m = a.merge(b);
assert_eq!(m.scope, ChangeScope::Part(1));
}
#[test]
fn merge_different_parts_becomes_global() {
let a = hint(ChangeScope::Part(0), false, false);
let b = hint(ChangeScope::Part(1), false, false);
let m = a.merge(b);
assert_eq!(m.scope, ChangeScope::Global);
}
#[test]
fn merge_measures_same_staff_extends_range() {
let a = hint(ChangeScope::Measures { part: 0, staff: 0, start: 2, end: 4 }, false, true);
let b = hint(ChangeScope::Measures { part: 0, staff: 0, start: 1, end: 3 }, false, true);
let m = a.merge(b);
assert_eq!(m.scope, ChangeScope::Measures { part: 0, staff: 0, start: 1, end: 4 });
}
#[test]
fn merge_measures_different_staff_becomes_global() {
let a = hint(ChangeScope::Measures { part: 0, staff: 0, start: 0, end: 1 }, false, true);
let b = hint(ChangeScope::Measures { part: 0, staff: 1, start: 0, end: 1 }, false, true);
let m = a.merge(b);
assert_eq!(m.scope, ChangeScope::Global);
}
}