use super::remark::RemarkIter;
use crate::{
core::{Command, CueStr, CueTimestamp, DataType, Track, TrackFlag, TrackIndex, TrackNo},
discid::isrc::Isrc,
error::{CueLibError, ParseError, ParseErrorKind},
internal::lexer::CueLexer,
probe::builder::TrackProbeBuilder,
};
#[derive(Clone)]
pub(super) struct TrackListProbe<'a> {
lexer: CueLexer<'a>,
initial_track: Track,
}
#[derive(Clone)]
pub(super) struct TrackIndexProbe<'a> {
lexer: CueLexer<'a>,
}
#[derive(Clone)]
pub struct TrackProbe<'a> {
pub(super) track: Track,
pub(super) flags: Option<TrackFlag>,
pub(super) isrc: Option<Isrc>,
pub(super) postgap: Option<CueTimestamp>,
pub(super) pregap: Option<CueTimestamp>,
pub(super) performer: Option<CueStr<'a>>,
pub(super) songwriter: Option<CueStr<'a>>,
pub(super) title: Option<CueStr<'a>>,
pub(super) sub_index_probe: TrackIndexProbe<'a>,
pub(super) start_index: CueTimestamp,
pub(super) pregap_index: Option<CueTimestamp>,
pub(super) track_buffer: &'a str,
}
pub struct Tracks<'a> {
lexer: CueLexer<'a>,
track: Option<Track>,
}
pub struct TrackSubIndexes<'a> {
lexer: CueLexer<'a>,
prev_index: Option<TrackIndex>,
}
impl<'a> TrackProbe<'a> {
#[inline]
pub const fn track_data_type(&self) -> DataType {
self.track.data_type
}
#[inline]
pub const fn track_no(&self) -> TrackNo {
self.track.no
}
#[inline]
pub const fn isrc(&self) -> Option<Isrc> {
self.isrc
}
#[inline]
pub const fn flags(&self) -> Option<TrackFlag> {
self.flags
}
#[inline]
pub const fn postgap(&self) -> Option<CueTimestamp> {
self.postgap
}
#[inline]
pub const fn pregap(&self) -> Option<CueTimestamp> {
self.pregap
}
#[inline]
pub const fn performer(&self) -> Option<CueStr<'a>> {
self.performer
}
#[inline]
pub const fn songwriter(&self) -> Option<CueStr<'a>> {
self.songwriter
}
#[inline]
pub const fn title(&self) -> Option<CueStr<'a>> {
self.title
}
#[inline]
pub const fn sub_indexes(&self) -> TrackSubIndexes<'a> {
self.sub_index_probe.iter()
}
#[inline]
pub const fn start_index(&self) -> CueTimestamp {
self.start_index
}
#[inline]
pub const fn pregap_index(&self) -> Option<CueTimestamp> {
self.pregap_index
}
#[inline]
pub fn remarks(&self) -> RemarkIter<'a> {
RemarkIter::new(self.track_buffer)
}
#[cfg(feature = "metadata")]
#[inline]
pub fn vorbis_comments(&self) -> crate::probe::vorbis_remark::VorbisRemarkIter<'a> {
self.remarks().into()
}
}
impl<'a> TrackListProbe<'a> {
#[inline]
pub(super) const fn iter(&self) -> Tracks<'a> {
Tracks {
lexer: self.lexer.snapshot(),
track: Some(self.initial_track),
}
}
#[inline]
pub(super) const fn new(lexer: CueLexer<'a>, initial_track: Track) -> Self {
Self {
lexer,
initial_track,
}
}
}
impl<'a> TrackIndexProbe<'a> {
#[inline]
const fn iter(&self) -> TrackSubIndexes<'a> {
TrackSubIndexes {
lexer: self.lexer.snapshot(),
prev_index: None,
}
}
}
impl<'a> Tracks<'a> {
pub fn next_track(&mut self) -> Result<Option<TrackProbe<'a>>, CueLibError> {
if let Some(curr_track) = self.track {
let index_probe = TrackIndexProbe {
lexer: self.lexer.snapshot(),
};
let mut builder = TrackProbeBuilder::new(index_probe, curr_track);
let track_buf_start = self.lexer.cursor_position();
let mut track_buf_end = track_buf_start;
'PARSER: loop {
match self.lexer.next_command()? {
Some(Command::Index { value }) => match value.no.into_inner() {
0 => builder.set_pregap_index(value.timestamp),
1 => builder.set_start_index(value.timestamp),
_ => Ok(()),
},
Some(Command::Remark { .. }) => Ok(()),
Some(Command::Flags { value }) => builder.set_flags(value),
Some(Command::ISRC { value }) => builder.set_isrc(value),
Some(Command::Performer { value }) => builder.set_performer(value),
Some(Command::Postgap { value }) => builder.set_postgap(value),
Some(Command::Pregap { value }) => builder.set_pregap(value),
Some(Command::SongWriter { value }) => builder.set_songwriter(value),
Some(Command::Title { value }) => builder.set_title(value),
Some(Command::Track { value }) => {
if curr_track.no.saturating_add(1) == value.no {
self.track = Some(value);
break 'PARSER;
} else {
Err(ParseErrorKind::InvalidTrackNo)
}
}
None => {
self.track = None;
break 'PARSER;
}
Some(_) => Err(ParseErrorKind::InvalidCommandUsage),
}
.map_err(|kind| ParseError::new_with_line(kind, self.lexer.position().line))?;
track_buf_end = self.lexer.cursor_position();
}
let track_buf = &self.lexer.as_raw_buffer()[track_buf_start..track_buf_end];
let probe = builder
.build(track_buf)
.map_err(|kind| ParseError::new_with_position(kind, self.lexer.position()))?;
Ok(Some(probe))
} else {
Ok(None)
}
}
}
impl<'a> TrackSubIndexes<'a> {
pub fn next_index(&mut self) -> Result<Option<TrackIndex>, CueLibError> {
loop {
match self.lexer.next_command()? {
Some(Command::Index { value }) => {
let is_valid = match self.prev_index {
Some(prev) => {
value.no == prev.no.saturating_add(1) && value.timestamp >= prev.timestamp
}
None => value.no.into_inner() <= 1,
};
if is_valid {
self.prev_index = Some(value);
match value.no.into_inner() {
0 | 1 => continue,
_ => return Ok(Some(value)),
};
} else {
let parse_error = ParseError::new_with_line(
ParseErrorKind::InvalidTrackIndex,
self.lexer.position().line,
);
return Err(parse_error.into());
}
}
Some(Command::Track { .. }) | None => {
return Ok(None);
}
Some(_) => continue,
}
}
}
}