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
//! Multi-line tracklists (SPEC R13): [`parse_tracklist`] and [`apply_edits`].
//!
//! A tracklist is the text people paste under a mix: one track per line, usually with a cue
//! time, sometimes with headers, links and notes in between. [`parse_tracklist`] gives one
//! [`TracklistEntry`] per line and never changes or drops a line; [`apply_edits`] rewrites the
//! text (set a time, shift, sort, insert, delete) and leaves every other byte alone.
//!
//! Guides: <https://4matic.github.io/trackparse/guides/tracklists/> and
//! <https://4matic.github.io/trackparse/guides/editing-tracklists/>.
//!
//! ```
//! use trackparse::tracklist::{parse_tracklist, EntryKind};
//!
//! let list = parse_tracklist("Tracklist:\n\n00:00 Intro\n01:30 Noisia - Stigma\n");
//! let kinds: Vec<EntryKind> = list.entries.iter().map(|e| e.kind).collect();
//! assert_eq!(kinds, [EntryKind::Header, EntryKind::Blank, EntryKind::Marker, EntryKind::Track]);
//! assert_eq!(list.stats.timed, 2);
//! assert!(list.warnings.is_empty());
//! ```
//!
//! Rule IDs (`R13.2`…) refer to the spec. The output is byte-identical to `trackparse/tracklist`
//! in the JavaScript package and `trackparse.tracklist` in Python.
pub use TracklistOptions;
pub use ;
use crateJson;
use crateToJson;
/// Parse a multi-line tracklist with the default options (R13).
///
/// The text is split at LF, CRLF and CR only; every line becomes one entry, and the entries'
/// `raw + eol`, joined, are the text again (R13.1.2). Never panics.
///
/// ```
/// use trackparse::tracklist::{parse_tracklist, EntryKind};
///
/// let list = parse_tracklist("00:00 Noisia - Stigma\n[03:45] Phace & Misanthrop - Desert Orgy\n");
/// assert_eq!(list.stats.tracks, 2);
/// let second = &list.entries[1];
/// assert_eq!(second.kind, EntryKind::Track);
/// assert_eq!(second.time.as_ref().map(|t| t.seconds), Some(225.0));
/// let track = second.track.as_ref().unwrap();
/// assert_eq!(track.artists.len(), 2);
/// assert_eq!(track.title, "Desert Orgy");
/// ```
/// [`parse_tracklist`] with options (R13.10).
///
/// ```
/// use trackparse::tracklist::{parse_tracklist_with, TracklistOptions, WarningCode};
///
/// let options = TracklistOptions::new().audio_seconds(3600.0);
/// let list = parse_tracklist_with("00:00 A - B\n1:02:03 C - D", &options);
/// assert_eq!(list.warnings[0].code, WarningCode::TimeAfterAudioEnd);
/// assert_eq!(list.warnings[0].line, 2);
/// ```
/// Apply edits to a tracklist text (R13.9), in order, each to the result of the previous one.
///
/// Lines no edit wrote stay byte-identical. Never panics: an edit that cannot be applied changes
/// nothing and is reported as [`EditWarningCode::EditInvalid`].
///
/// ```
/// use trackparse::tracklist::{apply_edits, Edit};
///
/// let text = "00:00 Noisia - Stigma\n03:45 Phace - Cold Champagne\n";
/// let edits = [
/// Edit::shift_from(30.0, 2),
/// Edit::set_rest(1, "Noisia - Stigma (Neosignal Remix)"),
/// ];
/// let result = apply_edits(text, &edits);
/// assert_eq!(result.text, "00:00 Noisia - Stigma (Neosignal Remix)\n04:15 Phace - Cold Champagne\n");
/// assert_eq!(result.changed, [1, 2]);
/// assert!(result.warnings.is_empty());
/// ```
/// [`apply_edits`] with options. The options are accepted for symmetry with the other ports
/// (R13.10); edits do not depend on them today.
///
/// ```
/// use trackparse::tracklist::{apply_edits, apply_edits_with, Edit, TracklistOptions};
///
/// let (text, edits) = ("00:00 A - B\n01:00 C - D", [Edit::shift(5.0)]);
/// let result = apply_edits_with(text, &edits, &TracklistOptions::new());
/// assert_eq!(result.text, "00:05 A - B\n01:05 C - D");
/// assert_eq!(result, apply_edits(text, &edits));
/// ```
/// `parseTracklist` for the JSON door (`wire`): loose options, the pipeline's own tracks.
pub
/// `applyEdits` for the JSON door (`wire`): `edits` is anything; only an array has edits.
pub