#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RunRole {
Alone,
Single,
Opens,
Continues,
}
#[derive(Debug, Clone, Copy)]
pub struct RecordOf<'a> {
pub album_id: Option<i64>,
pub album: &'a str,
pub album_artist: &'a str,
pub title: &'a str,
}
impl RecordOf<'_> {
fn joins(&self, first: &Self, id: Option<i64>) -> bool {
match (id, self.album_id) {
(Some(a), Some(b)) => a == b,
_ => self.album == first.album && self.album_artist == first.album_artist,
}
}
fn titled_as_record(&self) -> bool {
let fold = |s: &str| s.trim().to_lowercase();
fold(self.title) == fold(self.album)
}
}
pub fn record_runs(rows: &[RecordOf]) -> Vec<RunRole> {
let mut roles = Vec::with_capacity(rows.len());
let mut i = 0;
while i < rows.len() {
let first = &rows[i];
if first.album.is_empty() {
roles.push(RunRole::Alone);
i += 1;
continue;
}
let mut id = first.album_id;
let len = 1 + rows[i + 1..]
.iter()
.take_while(|r| {
let joins = r.joins(first, id);
id = id.or(r.album_id);
joins
})
.count();
if len == 1 && first.titled_as_record() {
roles.push(RunRole::Single);
} else {
roles.push(RunRole::Opens);
roles.extend(std::iter::repeat_n(RunRole::Continues, len - 1));
}
i += len;
}
roles
}
#[cfg(test)]
mod tests {
use super::*;
fn row<'a>(album_id: Option<i64>, album: &'a str, title: &'a str) -> RecordOf<'a> {
RecordOf {
album_id,
album,
album_artist: "Artist",
title,
}
}
#[test]
fn rows_next_to_each_other_on_one_record_are_one_run() {
use RunRole::*;
let rows = [
row(Some(1), "Record", "One"),
row(Some(1), "Record", "Two"),
row(None, "", "Loose"),
row(Some(2), "Song", " song "),
row(Some(3), "Other", "Three"),
row(Some(1), "Record", "Again"),
];
assert_eq!(
record_runs(&rows),
[Opens, Continues, Alone, Single, Opens, Opens]
);
}
#[test]
fn an_id_tells_editions_apart_and_a_missing_one_falls_back_to_the_names() {
use RunRole::*;
let rows = [
row(Some(1), "Record", "One"),
row(Some(2), "Record", "Two"),
row(None, "Record", "Three"),
row(Some(2), "Record", "Four"),
];
assert_eq!(record_runs(&rows), [Opens, Opens, Continues, Continues]);
let rows = [
row(None, "Record", "One"),
row(Some(1), "Record", "Two"),
row(Some(2), "Record", "Three"),
];
assert_eq!(record_runs(&rows), [Opens, Continues, Opens]);
}
}