/*!
# CDTOC: MusicBrainz
*/
use crate::{
hex,
ShaB64,
Toc,
};
impl Toc {
#[cfg_attr(docsrs, doc(cfg(feature = "musicbrainz")))]
#[expect(clippy::cast_possible_truncation, reason = "False positive.")]
#[must_use]
/// # MusicBrainz ID.
///
/// This returns the [MusicBrainz](https://musicbrainz.org/) ID
/// corresponding to the table of contents.
///
/// ## Examples
///
/// ```
/// use cdtoc::Toc;
///
/// let toc = Toc::from_cdtoc("4+96+2D2B+6256+B327+D84A").unwrap();
/// assert_eq!(
/// toc.musicbrainz_id().to_string(),
/// "nljDXdC8B_pDwbdY1vZJvdrAZI4-",
/// );
/// ```
pub fn musicbrainz_id(&self) -> ShaB64 {
use sha1::Digest;
// Start with a whole lotta ASCII zeroes.
let mut buf = crate::TRACK_ZEROES;
// Overwrite the first few entries with the leadout and audio sectors.
for (k, v) in std::iter::once(self.audio_leadout()).chain(self.audio_sectors().iter().copied()).enumerate() {
buf[k] = hex::upper_encode_u32(v);
}
// SHA and done!
let mut sha = sha1::Sha1::new();
sha.update(b"01");
sha.update(hex::upper_encode_u8(self.audio_len() as u8));
sha.update(buf.as_flattened());
ShaB64::from(sha)
}
#[cfg_attr(docsrs, doc(cfg(feature = "musicbrainz")))]
#[must_use]
/// # MusicBrainz URL.
///
/// This URL can be visited in a web browser to view the details for the
/// disc (if it is present in the database).
///
/// ## Examples
///
/// ```
/// use cdtoc::Toc;
///
/// let toc = Toc::from_cdtoc("4+96+2D2B+6256+B327+D84A").unwrap();
/// assert_eq!(
/// toc.musicbrainz_url(),
/// "https://musicbrainz.org/cdtoc/nljDXdC8B_pDwbdY1vZJvdrAZI4-",
/// );
/// ```
pub fn musicbrainz_url(&self) -> String {
let mut out = String::with_capacity(58);
out.push_str("https://musicbrainz.org/cdtoc/");
self.musicbrainz_id().push_to_string(&mut out);
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_musicbrainz() {
for (t, id) in [
(
"18+B6+3CE3+7C6F+B2BD+E47F+1121C+15865+175E0+1AED9+1E159+20BF9+235FC+259EF+2826E+29B62+2ED67+311B1+3396B+36ACB+3916B+3BB75+3D60A+40AA6+422FE+48B68+4E4CB",
"eLuEIkHsua.iJpetabxqYM9SIbk-",
),
(
"D+96+3B5D+78E3+B441+EC83+134F4+17225+1A801+1EA5C+23B5B+27CEF+2B58B+2F974+35D56+514C8",
"ucgpiD84p.2iBxO4j3hdjSjhtnw-",
),
(
"4+96+2D2B+6256+B327+D84A",
"nljDXdC8B_pDwbdY1vZJvdrAZI4-",
),
(
"10+B6+5352+62AC+99D6+E218+12AC0+135E7+142E9+178B0+19D22+1B0D0+1E7FA+22882+247DB+27074+2A1BD+2C0FB",
"PQ02DnwdDaxgWEFSpAzI_IVBL3o-",
),
(
"15+247E+2BEC+4AF4+7368+9704+B794+E271+110D0+12B7A+145C1+16CAF+195CF+1B40F+1F04A+21380+2362D+2589D+2793D+2A760+2DA32+300E1+32B46",
"JTsyXbyn9DUbppDWELj5o5CiFaI-",
),
(
"63+96+12D9+5546+A8A2+CAAA+128BF+17194+171DF+1722A+17275+172C0+1730B+17356+173A1+173EC+17437+17482+174CD+17518+17563+175AE+175F9+17644+1768F+176DA+17725+17770+177BB+17806+17851+1789C+178E7+17932+1797D+179C8+17A13+17A5E+17AA9+17AF4+17B3F+17B8A+17BD5+17C20+17C6B+17CB6+17D01+17D4C+17D97+17DE2+17E2D+17E78+17EC3+17F0E+17F59+17FA4+17FEF+1803A+18085+180D0+1811B+18166+181B1+181FC+18247+18292+182DD+18328+18373+183BE+18409+18454+1849F+184EA+18535+18580+185CB+18616+18661+186AC+186F7+18742+1878D+187D8+18823+1886E+188B9+18904+1894F+1899A+189E5+18A30+18A7B+18AC6+18B11+18B5C+18BA7+18BF2+18C38+1ECDC+246E9",
"efFU9TD0IyDF3iME6KlK.rZJEaw-",
),
] {
let toc = Toc::from_cdtoc(t).expect("Invalid TOC");
let mb_id = toc.musicbrainz_id();
assert_eq!(mb_id.to_string(), id);
// Test decoding three ways.
assert_eq!(ShaB64::decode(id), Ok(mb_id));
assert_eq!(ShaB64::try_from(id), Ok(mb_id));
assert_eq!(id.parse::<ShaB64>(), Ok(mb_id));
}
}
}