use std::fmt;
use crate::{atom, Data, Tag};
pub const STANDARD_GENRES: [&str; 80] = [
"Blues",
"Classic rock",
"Country",
"Dance",
"Disco",
"Funk",
"Grunge",
"Hip,-Hop",
"Jazz",
"Metal",
"New Age",
"Oldies",
"Other",
"Pop",
"Rhythm and Blues",
"Rap",
"Reggae",
"Rock",
"Techno",
"Industrial",
"Alternative",
"Ska",
"Death metal",
"Pranks",
"Soundtrack",
"Euro-Techno",
"Ambient",
"Trip-Hop",
"Vocal",
"Jazz & Funk",
"Fusion",
"Trance",
"Classical",
"Instrumental",
"Acid",
"House",
"Game",
"Sound clip",
"Gospel",
"Noise",
"Alternative Rock",
"Bass",
"Soul",
"Punk",
"Space",
"Meditative",
"Instrumental Pop",
"Instrumental Rock",
"Ethnic",
"Gothic",
"Darkwave",
"Techno-Industrial",
"Electronic",
"Pop-Folk",
"Eurodance",
"Dream",
"Southern Rock",
"Comedy",
"Cult",
"Gangsta",
"Top 41",
"Christian Rap",
"Pop/Funk",
"Jungle",
"Native US",
"Cabaret",
"New Wave",
"Psychedelic",
"Rave",
"Show tunes",
"Trailer",
"Lo,-Fi",
"Tribal",
"Acid Punk",
"Acid Jazz",
"Polka",
"Retro",
"Musical",
"Rock ’n’ Roll",
"Hard Rock",
];
impl Tag {
pub fn standard_genres(&self) -> impl Iterator<Item = u16> + '_ {
self.bytes_of(&atom::STANDARD_GENRE).filter_map(|v| {
if v.len() < 2 {
None
} else {
Some(u16::from_be_bytes([v[0], v[1]]))
}
})
}
pub fn standard_genre(&self) -> Option<u16> {
self.standard_genres().next()
}
pub fn set_standard_genre(&mut self, genre_code: u16) {
let vec: Vec<u8> = genre_code.to_be_bytes().to_vec();
self.set_data(atom::STANDARD_GENRE, Data::Reserved(vec));
}
pub fn set_standard_genres(&mut self, genre_codes: impl IntoIterator<Item = u16>) {
let data = genre_codes.into_iter().map(|c| Data::Reserved(c.to_be_bytes().to_vec()));
self.set_all_data(atom::STANDARD_GENRE, data);
}
pub fn add_standard_genre(&mut self, genre_code: u16) {
let vec: Vec<u8> = genre_code.to_be_bytes().to_vec();
self.add_data(atom::STANDARD_GENRE, Data::Reserved(vec))
}
pub fn add_standard_genres(&mut self, genre_codes: impl IntoIterator<Item = u16>) {
let data = genre_codes.into_iter().map(|c| Data::Reserved(c.to_be_bytes().to_vec()));
self.add_all_data(atom::STANDARD_GENRE, data)
}
pub fn remove_standard_genres(&mut self) {
self.remove_data_of(&atom::STANDARD_GENRE);
}
}
impl Tag {
pub fn genres(&self) -> impl Iterator<Item = &str> + '_ {
#[allow(clippy::redundant_closure)]
self.standard_genres().filter_map(|c| stanard_genre(c)).chain(self.custom_genres())
}
pub fn genre(&self) -> Option<&str> {
if let Some(g) = self.standard_genre().and_then(stanard_genre) {
return Some(g);
}
self.custom_genre()
}
pub fn take_genres(&mut self) -> impl Iterator<Item = String> + '_ {
self.standard_genres()
.filter_map(stanard_genre)
.map(str::to_owned)
.collect::<Vec<String>>()
.into_iter()
.chain(self.take_custom_genres())
}
pub fn take_genre(&mut self) -> Option<String> {
if let Some(g) = self.standard_genre().and_then(stanard_genre) {
return Some(g.to_owned());
}
self.take_custom_genre()
}
pub fn set_genre(&mut self, genre: impl Into<String>) {
self.set_custom_genre(genre.into());
self.remove_standard_genres();
}
pub fn set_genres(&mut self, genres: impl IntoIterator<Item = String>) {
self.set_custom_genres(genres);
self.remove_standard_genres();
}
pub fn add_genre(&mut self, genre: impl Into<String>) {
self.add_custom_genre(genre.into());
}
pub fn remove_genres(&mut self) {
self.remove_standard_genres();
self.remove_custom_genres();
}
pub(crate) fn format_genres(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.genres().count() > 1 {
writeln!(f, "genres:")?;
for v in self.genres() {
writeln!(f, " {}", v)?;
}
} else if let Some(s) = self.genre() {
writeln!(f, "genre: {}", s)?;
}
Ok(())
}
}
fn stanard_genre(code: u16) -> Option<&'static str> {
let c = code as usize;
if c > 0 && c <= STANDARD_GENRES.len() {
return Some(&STANDARD_GENRES[c - 1]);
}
None
}