1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//! # cue_lib
//!
//! Simple cue sheet parsing library with `no_std` by default.
//!
//! cue_lib is mainly based on original CDRWIN[^cdrwin] document with some relaxed requirements:
//!
//! - The limit for `TRACK` and `INDEX` numbers has been increased to 255 instead of 99 *(wasting bits? in this
//! economy!?)*
//! - `POSTGAP` and `PREGAP` commands can appear in any order within a `TRACK`.
//! - `ISRC` command can appear before, after, in between `INDEX` commands.
//! - The `FILE` command must appear appear before any `TRACK` command but can appear anywhere
//! else.
//! - The `CATALOG` command is not limited to 13-digit UPC/EAN format, any valid
//! [`CueStr`]( `core::CueStr`) is accepted.
//! - When [`metadata`] feature is enabled, `REM` with Vorbis comments can be read as additional
//! metadata for both album and tracks.
//! - FLAC is allowed in the `FILE` command as a file type.
//!
//! <div class="warning">
//!
//! cue_lib does not support EAC's cue sheet with multiple `FILE` commands.
//!
//! </div>
//!
//! [^cdrwin]: <https://web.archive.org/web/20151023011544/http://digitalx.org/cue-sheet/syntax/index.html>
//!
//! ## Feature flags
//!
//! | Feature Name | Description |
//! |------------------------|------------------------------------------------------------------------------------------------- |
//! | **default** | Does not enable any additional feature (no_std) |
//! | **[alloc]** | Enables modules/types that uses dynamic memory allocation (e.g. [`parse`]) |
//! | **[metadata]** | Enables reading remarks as Vorbis comment, and provides helper types for ID3, Vorbis, and libav. |
//! | **serde** | Enables **serde**[^serde] only for type **serialization**. |
//! | **[ean](discid::ean)** | Provides [Ean13](discid::ean::Ean13) utility type to parse 13 digits EAN-13[^ean13] catalog codes.|
//! | **[upc](discid::upc)** | Provides [`UpcA`](discid::upc::UpcA) utility type to parse 12 digits UPC-A[^upca] catalog codes. |
//!
//! [^serde]: <https://serde.rs/>
//! [^ean13]: <https://en.wikipedia.org/wiki/International_Article_Number>
//! [^upca]: <https://en.wikipedia.org/wiki/Universal_Product_Code>
//!
//! ## Example - CuesheetParser
//!
//! <div class="warning">
//!
//! Requires **`alloc`** feature
//!
//! </div>
//!
//! ```
//! use cue_lib::error::CueLibError;
//! use cue_lib::parse::CuesheetParser;
//!
//! let cuesheet = r#"
//! PERFORMER "Rick Astley"
//! TITLE "Whenever You Need Somebody"
//! FILE "rick_astley_whenever_you_need_somebody.flac" WAVE
//! TRACK 01 AUDIO
//! TITLE "Never Gonna Give You Up"
//! PERFORMER "Rick Astley"
//! INDEX 00 00:00:00
//! INDEX 01 00:03:32
//! ISRC GBAYE0100001
//! TRACK 02 AUDIO
//! TITLE "Never Gonna Let You Down"
//! PERFORMER "Rick Astley"
//! INDEX 01 03:32:00
//! INDEX 02 04:32:00
//! ISRC GBAYE0100002
//! TRACK 03 AUDIO
//! TITLE "Never Gonna Run Around and Desert You"
//! PERFORMER "Rick Astley"
//! INDEX 01 07:04:00
//! ISRC GBAYE0100003
//! "#;
//!
//! fn print_cue(cuesheet_str: &str) -> Result<(), CueLibError> {
//! let cuesheet = CuesheetParser::new().parse(cuesheet_str)?;
//!
//! if let Some(title) = cuesheet.album_title {
//! println!("Title: {}", title);
//! }
//!
//! if let Some(performer) = cuesheet.performer {
//! println!("Performer: {}", performer);
//! }
//!
//! for track in cuesheet.tracks.iter() {
//! print!("* Track - {}", track.no);
//!
//! if let Some(track_title) = track.title {
//! println!(" {}", track_title);
//! }
//!
//! // Calculated track duration, only the last track's duration is `None` since cue_lib
//! // does not check FILE duration.
//! if let Some(track_duration) = track.time_info.duration {
//! println!(" Duration {}s", track_duration.as_duration().as_secs());
//! }
//!
//! // INDEX 00 (optional) often represent the silence at the start of the song.
//! if let Some(index_zero) = track.time_info.pregap_start {
//! println!(" Index 00 at {}s", index_zero.as_duration().as_secs());
//! }
//!
//! // INDEX 01 (required) actual start of the track.
//! println!(
//! " Index 01 at {}s ",
//! track.time_info.start.as_duration().as_secs()
//! );
//!
//! // To access other sub-indexes INDEX 02, 03 etc.
//! for sub_index in track.sub_indexes.iter() {
//! println!(
//! " Index {} starts at {}s",
//! sub_index.no,
//! sub_index.timestamp.as_duration().as_secs()
//! );
//! }
//! }
//!
//! Ok(())
//! }
//!
//! print_cue(cuesheet);
//! ```
//!
//! ## Example - CuesheetProbe
//!
//! Low level API without any dynamic memory allocation. It basically attaches to cuesheet
//! and allows progressively reading its content.
//!
//! For more detailed explanation see [`probe`] module.
//!
//! ```
//! use cue_lib::probe::CuesheetProbe;
//! use cue_lib::error::CueLibError;
//!
//! let cuesheet = r#"
//! PERFORMER "Rick Astley"
//! TITLE "Whenever You Need Somebody"
//! FILE "rick_astley_whenever_you_need_somebody.flac" WAVE
//! TRACK 01 AUDIO
//! TITLE "Never Gonna Give You Up"
//! PERFORMER "Rick Astley"
//! INDEX 00 00:00:00
//! INDEX 01 00:03:32
//! ISRC GBAYE0100001
//! TRACK 02 AUDIO
//! TITLE "Never Gonna Let You Down"
//! PERFORMER "Rick Astley"
//! INDEX 01 03:32:00
//! INDEX 02 04:32:00
//! ISRC GBAYE0100002
//! TRACK 03 AUDIO
//! TITLE "Never Gonna Run Around and Desert You"
//! PERFORMER "Rick Astley"
//! INDEX 01 07:04:00
//! ISRC GBAYE0100003
//! "#;
//!
//! fn print_cue(cuesheet: &str) -> Result<(), CueLibError> {
//! let probe = CuesheetProbe::new(cuesheet)?;
//!
//! if let Some(title) = probe.album_title() {
//! println!("Title: {}", title);
//! }
//!
//! if let Some(performer) = probe.performer() {
//! println!("Performer: {}", performer);
//! }
//!
//! let mut tracks = probe.tracks();
//!
//! while let Some(track) = tracks.next_track()? {
//! print!("* Track - {}", track.track_no());
//!
//! if let Some(track_title) = track.title() {
//! println!(" {}", track_title);
//! }
//!
//! // INDEX 00 (optional) often represent the silence at the start of the song.
//! if let Some(index_zero) = track.pregap_index() {
//! println!(" Index 00 at {} sec", index_zero.as_duration().as_secs());
//! }
//!
//! // INDEX 01 (required) actual start of the track.
//! println!(
//! " Index 01 at {} sec",
//! track.start_index().as_duration().as_secs()
//! );
//!
//! // To access other sub-indexes INDEX 02, 03 etc.
//! let mut indexes = track.sub_indexes();
//!
//! while let Some(sub_index) = indexes.next_index()? {
//! println!(
//! " Index {} starts at {} sec",
//! sub_index.no,
//! sub_index.timestamp.as_duration().as_secs()
//! );
//! }
//! }
//!
//! Ok(())
//! }
//!
//! print_cue(cuesheet);
//! ```
extern crate alloc;
/// Core data types related to cue sheets
/// Disc identifiers commonly used in cue sheets
/// Error handling for the library
/// Handles metadata mappings including ID3, Vorbis, and AvLib formats
/// Simple parser implementation for cue sheet files
/// Low-level API to parse cue sheet data