Skip to main content

Crate trackparse

Crate trackparse 

Source
Expand description

Turn messy track names into structured data: artists with roles, remixes and versions, junk, and whole tracklists.

trackparse is driven by a written spec. Behaviour is defined by SPEC.md and its shared fixtures. This crate passes every shared fixture, and for every fixture input its output is byte-identical to the JavaScript (npm trackparse) and Python (PyPI trackparse) packages. Rule IDs such as R6.2 in these docs refer to the spec.

§Single tracks

parse takes any string and returns a ParsedTrack. It never panics and never returns an error: what it recognises goes into a field, what it does not stays in the title.

use trackparse::{parse, ArtistRole, JunkKind, VersionType};

let track = parse("Noisia feat. Foreign Beggars - Shellshock (Rockwell Remix) [Official Video]");

assert_eq!(track.artists[0].name, "Noisia");
assert_eq!(track.artists[1].name, "Foreign Beggars");
assert_eq!(track.artists[1].role, ArtistRole::Featured);
assert_eq!(track.title, "Shellshock");
assert_eq!(track.full_title, "Shellshock (Rockwell Remix)");
assert_eq!(track.versions[0].version_type, VersionType::Remix);
assert_eq!(track.versions[0].artists[0].name, "Rockwell");
assert_eq!(track.junk[0].kind, JunkKind::Video);

parse_with takes ParseOptions; a Parser keeps resolved options for many calls. parse_artists splits a credit string, format_track writes a track back as a string, normalize() cleans a string without parsing it, and all_artists lists every credited name, remixers included.

use trackparse::{format_track_with, parse_with, FeatPlacement, FormatOptions, ParseOptions};

let options = ParseOptions::new().known_artists(["Chase & Status"]);
let track = parse_with("Chase & Status - Blind Faith ft. Liam Bailey", &options);
assert_eq!(track.artists.len(), 2);

let format = FormatOptions::new().feat(FeatPlacement::Artist);
assert_eq!(format_track_with(&track, &format), "Chase & Status ft. Liam Bailey - Blind Faith");

§Tracklists

The tracklist module reads a whole pasted tracklist: one entry per line, with its time, its kind and the parsed track, plus warnings about the times. tracklist::apply_edits rewrites times and lines without touching anything else.

use trackparse::tracklist::{apply_edits, parse_tracklist, Edit, WarningCode};

let text = "00:00 Noisia - Stigma\n03:45 Phace & Misanthrop - Desert Orgy\n02:10 Mefjus - Suicide Bassline";
let list = parse_tracklist(text);
assert_eq!(list.stats.tracks, 3);
assert_eq!(list.entries[1].time.as_ref().unwrap().seconds, 225.0);
assert_eq!(list.entries[1].track.as_ref().unwrap().title, "Desert Orgy");
assert_eq!(list.warnings[0].code, WarningCode::TimeOutOfOrder);

let fixed = apply_edits(text, &[Edit::sort_by_time()]);
assert_eq!(
    fixed.text,
    "00:00 Noisia - Stigma\n02:10 Mefjus - Suicide Bassline\n03:45 Phace & Misanthrop - Desert Orgy"
);

§JSON

Fields are snake_case in Rust. to_json() on every output type writes the spec’s JSON form: camelCase keys (fullTitle, unknownArtist), every key present, in the schema’s order. It is the same text the JavaScript and Python packages produce, and needs no dependency.

let artists = trackparse::parse_artists("Noisia & Phace");
assert_eq!(
    artists[1].to_json(),
    r#"{"name":"Phace","role":"primary","joiner":"&","source":"artist"}"#
);

With the serde feature the output types implement Serialize and Deserialize with the same keys, so serde_json::to_string(&track) gives the text of track.to_json(). The options and tracklist::Edit implement both traits too. Numbers are written as to_json() writes them. The exceptions are numbers no parse result contains: seconds from 0.000001 up to 0.00001 (and, with older serde_json releases, of 1e21 and more) in an Edit or an option built by hand, which serde_json writes in its own float notation (1e-6 for 0.000001). Non-finite seconds are written as null, as by to_json(), and null reads back as NaN. Keyword maps are written without repeated keys (the last value wins, as in a JS object).

§Guarantees

  • Total: any &str gives a result. No panics, no Result (R0.8).
  • No regular expressions and no backtracking: the scanners make a bounded number of passes over the input. A 10,000-code-point string parses in a few milliseconds, and the test suite checks that repeated hostile fragments scale far below quadratic.
  • No unsafe. One dependency (unicode-normalization); serde only with the feature.
  • The Unicode tables the rules depend on (letters, uppercase letters, lowercase mapping) are compiled in, so the output does not change with the compiler version.
  • NFC and NFD (R1, R0.4) come from unicode-normalization and follow that crate’s Unicode version: 0.1.26, the minimum this crate accepts, has the Unicode 18 tables. The JavaScript reference normalizes with its runtime’s tables (Unicode 17 on Node 24), so a string with one of the 32 combining marks added in Unicode 18 can come out in another mark order than it does there. Text without those marks is not affected.

Modules§

json
A small JSON value with a reader and a writer. The crate has no JSON dependency: this is what to_json() writes with, and what the fixtures and the cross-port diff read.
tracklist
Multi-line tracklists (SPEC R13): parse_tracklist and apply_edits.

Structs§

Artist
One credited artist.
Flags
Yes/no facts about the track.
FormatOptions
Options of format_track_with (R11).
Junk
A fragment that says nothing about the track: Official Video, HD, Free Download. It is taken out of the title and kept here (the parser extracts, it does not delete).
KeywordOptions
Extra vocabulary, added to the built-in word lists (R12). Keys are matched with ASCII lowercasing.
ParseOptions
Options of parse_with, parse_artists_with and Parser (R12). The default is what parse uses.
ParsedTrack
The result of parse: everything recognised in one track string.
Parser
A parser with its options resolved and its vocabulary tables built once (createParser in JavaScript, create_parser in Python). Use it to parse many strings with the same options. It is Send + Sync, and cloning it is cheap (the tables are shared).
Position
A track number in front of the string (R4.2).
Timestamp
A cue time in front of a single track string (R4.1), in whole seconds.
UnknownName
The error of FromStr on the crate’s enums: the string is not one of the spec’s names.
Version
One version of the track: a remix, an edit, a live take, a remaster (R5, R8).

Enums§

ArtistRole
What an artist did on the track (R7).
ArtistSource
Where in the string an artist was found: the artist side, the title side, or a version group.
FeatPlacement
Where format_track_with writes featured artists (R11.2).
JoinerStyle
Joiners as written, or mapped to their canonical form (R11.5).
JunkKind
What kind of noise a junk fragment is (R5).
Mode
The mode a string was parsed in (R2). Tracklist is only used when asked for (R13.6); auto never resolves to it.
ModeOption
The mode to parse in (R2). Auto picks clean, youtube or filename from the string itself, never tracklist.
SplitAnd
When the word and splits a credit list (R7.3). & and the other joiners always split.
VersionDelimiter
How a version was written: in a bracket group, or after a dash.
VersionType
The type of a version (R5): remix, edit, live, remaster and so on.
VersionsOption
Whether format_track_with writes the versions (R11.3).
Warning
A warning of the single-track parser: the result is a guess the caller may want to check.

Constants§

SPEC_VERSION
The version of the spec this crate implements.

Traits§

ToJson
Conversion to the spec’s JSON form (camelCase keys, every key present, schema order).

Functions§

all_artists
All credited names: the track’s artists plus every version’s remixers, deduped (R0.4 key).
format_track
Render a parsed track back to a string (R11) with the default format options.
format_track_with
Render a parsed track back to a string (R11).
normalize
Normalize a track string (R1): NFC, ASCII brackets, quotes and dashes, invisible characters removed, whitespace collapsed. Idempotent.
parse
Parse one track string with the default options (mode auto).
parse_artists
Split a credit string into artists (R10).
parse_artists_with
parse_artists with options (known_artists, split_and, keywords).
parse_with
Parse one track string with options (R12).