pub(crate) fn file_type_name(ft: Option<i64>) -> &'static str {
match ft {
Some(0) => "MP3",
Some(1) => "M4A",
Some(4) => "WAV",
Some(5) => "FLAC",
Some(11) => "AIFF",
Some(19) => "SoundCloud",
Some(21) => "Beatport",
Some(25) => "Spotify",
Some(26) => "Apple Music",
Some(_) => "unknown",
None => "-",
}
}
const STREAMING_FILE_TYPES: &[i64] = &[19, 21, 25, 26];
const CLOUD_SERVICE_ID: i64 = 2;
pub(crate) struct TrackFacts {
pub origin: Origin,
pub file_type: Option<i64>,
pub cue_count: i64,
pub locked: bool,
pub present: Option<bool>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum Origin {
Local,
Cloud,
Stream,
}
impl Origin {
fn tag(&self) -> &'static str {
match self {
Self::Local => "local",
Self::Cloud => "cloud",
Self::Stream => "stream",
}
}
}
pub(crate) fn track_tags(f: TrackFacts) -> String {
let mut tags: Vec<&str> = Vec::new();
tags.push(f.origin.tag());
match f.present {
Some(true) => tags.push("present"),
Some(false) => tags.push("missing"),
None => {}
}
match f.file_type {
Some(0) => tags.extend(["mp3", "lossy"]),
Some(1) => tags.extend(["m4a", "lossy"]),
Some(4) => tags.extend(["wav", "lossless"]),
Some(5) => tags.extend(["flac", "lossless"]),
Some(11) => tags.extend(["aiff", "lossless"]),
_ => {}
}
if f.cue_count > 0 {
tags.push("cues");
}
if f.locked {
tags.push("locked");
}
format!(" {} ", tags.join(" "))
}
pub(crate) fn origin(
file_type: Option<i64>,
folder_path: Option<&str>,
service_id: Option<i64>,
) -> Origin {
let has_path = folder_path.is_some_and(|p| p.contains('/') || p.contains('\\'));
if file_type.is_some_and(|ft| STREAMING_FILE_TYPES.contains(&ft)) || !has_path {
Origin::Stream
} else if service_id == Some(CLOUD_SERVICE_ID) {
Origin::Cloud
} else {
Origin::Local
}
}
pub(crate) fn format_bpm(bpm: Option<i64>) -> String {
match bpm {
Some(v) => format!("{:.2}", v as f64 / 100.0),
None => "-".into(),
}
}
pub(crate) fn format_length(secs: Option<i64>) -> String {
match secs {
Some(s) => format!("{}:{:02} ({s}s)", s / 60, s % 60),
None => "-".into(),
}
}
pub(crate) fn format_msec(ms: i64) -> String {
let total_secs = ms / 1000;
let frac = ms % 1000;
format!("{}:{:02}.{:03}", total_secs / 60, total_secs % 60, frac)
}
pub(crate) fn kind_label(kind: Option<i64>) -> &'static str {
match kind {
Some(0) => "memory",
Some(k) if (1..=16).contains(&k) => match k {
1 => "hot A",
2 => "hot B",
3 => "hot C",
4 => "hot D",
5 => "hot E",
6 => "hot F",
7 => "hot G",
8 => "hot H",
9 => "hot I",
10 => "hot J",
11 => "hot K",
12 => "hot L",
13 => "hot M",
14 => "hot N",
15 => "hot O",
16 => "hot P",
_ => unreachable!(),
},
_ => "?",
}
}
#[cfg(test)]
mod tests {
use super::*;
fn facts(ft: Option<i64>, path: Option<&str>, sid: Option<i64>) -> TrackFacts {
TrackFacts {
origin: origin(ft, path, sid),
file_type: ft,
cue_count: 0,
locked: false,
present: None,
}
}
fn tags(ft: i64, path: &str, sid: i64) -> String {
track_tags(facts(Some(ft), Some(path), Some(sid)))
}
#[test]
fn a_service_uri_is_a_stream_however_it_is_spelled() {
assert!(tags(19, "soundcloud:tracks:1011732511", 0).contains(" stream "));
assert!(tags(26, "apple-music:tracks:1083723407", 0).contains(" stream "));
assert!(tags(25, "spotify:track:0BLAbNPYQspTAF7mKmS4Kb", 0).contains(" stream "));
assert!(tags(21, "/bs/catalog/tracks/6238554/", 0).contains(" stream "));
assert!(track_tags(facts(None, None, None)).contains(" stream "));
}
#[test]
fn a_path_is_local_whichever_machine_wrote_it() {
assert!(tags(5, "/Users/x/Music/a.flac", 0).contains(" local "));
assert!(tags(1, "C:/users/x/Music/a.mp3", 0).contains(" local "));
assert!(tags(11, "D:/Contents/a.aiff", 0).contains(" local "));
}
#[test]
fn cloud_sync_is_its_own_origin_not_a_local_file() {
let cloud = tags(5, "/contents_2768718261/artist/album/01.flac", 2);
assert!(cloud.contains(" cloud "));
assert!(!cloud.contains(" local "));
assert!(!cloud.contains(" stream "));
assert!(cloud.contains(" flac ") && cloud.contains(" lossless "));
assert!(tags(1, "C:/Users/x/Music/contents_4204620759/a.m4a", 2).contains(" cloud "));
}
#[test]
fn a_stream_carries_no_format_keyword() {
let stream = tags(19, "soundcloud:tracks:1", 0);
assert!(!stream.contains(" lossy "));
assert!(!stream.contains(" lossless "));
}
#[test]
fn cues_and_lock_are_tagged_only_when_present() {
let bare = track_tags(facts(Some(5), Some("/a/b.flac"), Some(0)));
assert!(!bare.contains(" cues ") && !bare.contains(" locked "));
let full = track_tags(TrackFacts {
cue_count: 4,
locked: true,
..facts(Some(5), Some("/a/b.flac"), Some(0))
});
assert!(full.contains(" cues ") && full.contains(" locked "));
}
#[test]
fn presence_is_tagged_only_when_it_could_be_checked() {
let unchecked = facts(Some(5), Some("/a/b.flac"), Some(0));
let tags = track_tags(unchecked);
assert!(!tags.contains(" present ") && !tags.contains(" missing "));
let here = track_tags(TrackFacts {
present: Some(true),
..facts(Some(5), Some("/a/b.flac"), Some(0))
});
assert!(here.contains(" present ") && !here.contains(" missing "));
let gone = track_tags(TrackFacts {
present: Some(false),
..facts(Some(5), Some("/a/b.flac"), Some(0))
});
assert!(gone.contains(" missing ") && !gone.contains(" present "));
assert!(gone.contains(" local ") && gone.contains(" flac "));
}
}