app_lib/
audio_extensions.rs

1//! Canonical audio sample extensions for the library scanner, unified walker,
2//! file watcher, and Settings → App Info. Keep in one place so UI labels match indexing.
3
4/// Lowercase extensions with a leading dot (matches `audio_scanner` path handling).
5pub const AUDIO_EXTENSIONS: &[&str] = &[
6    ".wav", ".mp3", ".aiff", ".aif", ".flac", ".ogg", ".m4a", ".wma", ".aac", ".opus", ".rex",
7    ".rx2", ".sf2", ".sfz",
8];
9
10/// `Path::extension()` lowercased, no dot — e.g. `"wav"`, `"m4a"`.
11#[inline]
12pub fn is_audio_extension_lowercase(ext_no_dot: &str) -> bool {
13    AUDIO_EXTENSIONS
14        .iter()
15        .any(|e| e.strip_prefix('.') == Some(ext_no_dot))
16}
17
18/// Uppercase tags for Settings → App Info (no leading dot).
19pub fn audio_format_tags_for_app_info() -> Vec<String> {
20    AUDIO_EXTENSIONS
21        .iter()
22        .map(|e| {
23            e.strip_prefix('.')
24                .unwrap_or(e)
25                .to_ascii_uppercase()
26        })
27        .collect()
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn every_dotted_ext_maps_to_lowercase_predicate() {
36        for d in AUDIO_EXTENSIONS {
37            let plain = d.strip_prefix('.').expect("extension must start with '.'");
38            assert!(
39                is_audio_extension_lowercase(plain),
40                "is_audio_extension_lowercase({plain})"
41            );
42        }
43    }
44
45    #[test]
46    fn tags_align_with_extension_count() {
47        assert_eq!(
48            audio_format_tags_for_app_info().len(),
49            AUDIO_EXTENSIONS.len()
50        );
51    }
52}