#[cfg(feature = "metadata")]
use crate::metadata::{map::MetadataMap, vorbis::VorbisTag};
use crate::{
core::{AlbumFile, CueStr, CueTimestamp, DataType, TrackFlag, TrackIndex, TrackNo},
discid::isrc::Isrc,
error::CueLibError,
probe::{
CuesheetProbe,
track::{TrackSubIndexes, Tracks},
},
};
use alloc::vec::Vec;
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug)]
pub struct Cuesheet<'a> {
pub catalog: Option<CueStr<'a>>,
pub cdtextfile: Option<CueStr<'a>>,
pub file: Option<AlbumFile<'a>>,
pub performer: Option<CueStr<'a>>,
pub songwriter: Option<CueStr<'a>>,
pub album_title: Option<CueStr<'a>>,
pub tracks: Vec<TrackInfo<'a>>,
#[cfg(feature = "metadata")]
pub remark_metadata: MetadataMap<'a, VorbisTag>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug)]
pub struct TrackInfo<'a> {
pub data_type: DataType,
pub flags: Option<TrackFlag>,
pub isrc: Option<Isrc>,
pub performer: Option<CueStr<'a>>,
pub postgap: Option<CueTimestamp>,
pub pregap: Option<CueTimestamp>,
pub songwriter: Option<CueStr<'a>>,
pub sub_indexes: Vec<TrackIndex>,
pub time_info: TimeInfo,
pub title: Option<CueStr<'a>>,
pub no: TrackNo,
#[cfg(feature = "metadata")]
pub remark_metadata: MetadataMap<'a, VorbisTag>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Default, Debug)]
pub struct TimeInfo {
pub start: CueTimestamp,
pub end: Option<CueTimestamp>,
pub pregap_start: Option<CueTimestamp>,
pub duration: Option<CueTimestamp>,
}
#[derive(Default)]
pub struct CuesheetParser {
#[cfg(feature = "metadata")]
allow_vorbis_remarks: bool,
}
impl CuesheetParser {
#[inline]
pub const fn new() -> Self {
Self {
allow_vorbis_remarks: false,
}
}
#[cfg(feature = "metadata")]
#[inline]
pub const fn allow_vorbis_remarks(mut self, value: bool) -> Self {
self.allow_vorbis_remarks = value;
self
}
pub fn parse<'a>(self, input: &'a str) -> Result<Cuesheet<'a>, CueLibError> {
let probe = CuesheetProbe::new(input)?;
let mut cuesheet = Cuesheet {
catalog: probe.catalog(),
cdtextfile: probe.cdtextfile(),
file: probe.file_info(),
performer: probe.performer(),
songwriter: probe.songwriter(),
album_title: probe.album_title(),
tracks: Vec::new(),
#[cfg(feature = "metadata")]
remark_metadata: MetadataMap::new(),
};
#[cfg(feature = "metadata")]
{
if self.allow_vorbis_remarks {
cuesheet.remark_metadata = MetadataMap::from_iter(probe.vorbis_comments());
}
};
self.process_tracks(cuesheet, probe.tracks())
}
#[inline]
fn process_tracks<'a>(
&self,
mut cuesheet: Cuesheet<'a>,
mut track_probe: Tracks<'a>,
) -> Result<Cuesheet<'a>, CueLibError> {
while let Some(track) = track_probe.next_track()? {
let mut track_info = TrackInfo {
data_type: track.track_data_type(),
flags: track.flags(),
isrc: track.isrc(),
performer: track.performer(),
postgap: track.postgap(),
pregap: track.pregap(),
songwriter: track.songwriter(),
sub_indexes: Vec::new(),
time_info: TimeInfo {
start: track.start_index(),
pregap_start: track.pregap_index(),
end: None,
duration: None,
},
title: track.title(),
no: track.track_no(),
#[cfg(feature = "metadata")]
remark_metadata: crate::metadata::map::MetadataMap::new(),
};
Self::process_sub_indexes(&mut track_info, track.sub_indexes())?;
#[cfg(feature = "metadata")]
{
if self.allow_vorbis_remarks {
track_info.remark_metadata = MetadataMap::from_iter(track.vorbis_comments());
}
}
cuesheet.tracks.push(track_info);
}
Self::calc_track_times(&mut cuesheet.tracks);
Ok(cuesheet)
}
fn process_sub_indexes<'a>(
track: &mut TrackInfo<'a>,
mut indexes: TrackSubIndexes<'a>,
) -> Result<(), CueLibError> {
while let Some(index) = indexes.next_index()? {
track.sub_indexes.push(index);
}
Ok(())
}
#[inline]
fn calc_track_times(tracks: &mut Vec<TrackInfo>) {
let mut track_iter = tracks.iter_mut().peekable();
while let Some(track) = track_iter.next() {
if let Some(next_track) = track_iter.peek() {
let end = if let Some(pregap) = next_track.time_info.pregap_start {
pregap
} else {
next_track.time_info.start
};
let duration = end.as_millis() - track.time_info.start.as_millis();
track.time_info.end = Some(end);
track.time_info.duration = Some(CueTimestamp::from_millis(duration));
}
}
}
}