use crate::catalog::models::{author::Author, opus::Opus};
#[non_exhaustive]
pub enum Attribution {
Both {
author: Author,
opus: Opus,
},
AuthorOnly(Author),
OpusOnly(Opus),
}
impl Attribution {
#[must_use]
pub fn from_parts(
forename: Option<&str>,
surname: Option<&str>,
title: Option<&str>,
) -> Option<Self> {
let given = forename.map(str::trim).filter(|n| !n.is_empty());
let family = surname.map(str::trim).filter(|n| !n.is_empty());
let author = match (given, family) {
(Some(g), Some(f)) => Some(Author::new(g, f)),
(None, Some(f)) => Some(Author::surname_only(f)),
(Some(g), None) => Some(Author::forename_only(g)),
(None, None) => None,
};
let opus = title
.map(str::trim)
.filter(|t| !t.is_empty())
.map(Opus::new);
match (author, opus) {
(Some(author), Some(opus)) => Some(Attribution::Both { author, opus }),
(Some(author), None) => Some(Attribution::AuthorOnly(author)),
(None, Some(opus)) => Some(Attribution::OpusOnly(opus)),
(None, None) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_parts_selects_the_right_variant() {
assert!(matches!(
Attribution::from_parts(Some("Charles"), Some("Dickens"), Some("Oliver Twist")),
Some(Attribution::Both { .. })
));
assert!(matches!(
Attribution::from_parts(None, Some("Dickens"), None),
Some(Attribution::AuthorOnly(_))
));
assert!(matches!(
Attribution::from_parts(None, None, Some("Bear")),
Some(Attribution::OpusOnly(_))
));
assert!(Attribution::from_parts(None, None, None).is_none());
}
#[test]
fn blank_parts_are_absent() {
let Some(Attribution::AuthorOnly(author)) =
Attribution::from_parts(Some("Prince"), Some(" "), None)
else {
panic!("expected an author-only attribution");
};
assert_eq!(author.forename(), Some("Prince"));
assert_eq!(author.surname(), None);
assert!(Attribution::from_parts(Some(""), Some(" "), Some("\t")).is_none());
}
#[test]
fn parts_are_trimmed() {
let Some(Attribution::Both { author, opus }) =
Attribution::from_parts(Some(" Charles "), Some(" Dickens"), Some(" Oliver "))
else {
panic!("expected a both attribution");
};
assert_eq!(author.forename(), Some("Charles"));
assert_eq!(author.surname(), Some("Dickens"));
assert_eq!(opus.title(), "Oliver");
}
}