rustriff_lib/infrastructure/file_loader/file_loader_trait.rs
1use std::path::Path;
2
3/// Abstraction over filesystem operations required by the IR profile pipeline.
4///
5/// The trait is `Send + Sync` so it can be injected into services that may be
6/// shared across threads (e.g. Tauri's managed state).
7///
8/// A real implementation based on [`std::fs`] and the [`hound`] WAV crate is
9/// provided by [`FileLoader`].
10///
11/// [`FileLoader`]: super::FileLoader
12pub trait FileLoaderTrait: Send + Sync {
13 /// Reads the sample rate declared in the WAV header at `path`.
14 /// Returns `None` if the file cannot be opened or parsed (missing, corrupt,
15 /// unsupported format). The caller should treat `None` as "assume DSP rate
16 /// and skip resampling".
17 fn read_wav_sample_rate(&self, path: &Path) -> Option<u32>;
18
19 /// Decodes a WAV file into a flat mono `f32` sample buffer.
20 ///
21 /// **Multi-channel handling**: Stereo and multi-channel WAV files are
22 /// automatically downmixed to mono by averaging all channels in each frame.
23 /// For example, a stereo file with interleaved `[L0, R0, L1, R1]` samples
24 /// produces `[(L0+R0)/2, (L1+R1)/2]`.
25 ///
26 /// **Normalization**: Integer samples are normalized to `[-1.0, 1.0]` using
27 /// the full integer range determined by `bits_per_sample`.
28 ///
29 /// Returns an empty `Vec` and logs a warning on any I/O or parse error so
30 /// that the caller can fall back to passthrough rather than panicking.
31 fn read_wav_to_buffer(&self, path: &Path) -> Vec<f32>;
32
33 /// Returns a sorted list of `.wav` filenames (not full paths) found in
34 /// `directory`.
35 ///
36 /// Only regular files with a `.wav` extension (case-insensitive) are
37 /// included; subdirectories and other file types are silently skipped.
38 ///
39 /// Returns `Err` when the directory itself cannot be read.
40 fn list_ir_profile_file_names(&self, directory: &Path) -> Result<Vec<String>, String>;
41
42 /// Creates `directory` and all missing parent components if they do not
43 /// already exist (`mkdir -p` semantics).
44 fn ensure_directory(&self, directory: &Path) -> Result<(), String>;
45
46 /// Writes `bytes` to `path`, truncating any existing file.
47 fn write_file_bytes(&self, path: &Path, bytes: &[u8]) -> Result<(), String>;
48
49 /// Removes the file at `path`.
50 ///
51 /// Returns `Err` when the file does not exist or cannot be removed.
52 fn remove_file(&self, path: &Path) -> Result<(), String>;
53
54 /// Validates that raw bytes represent a usable cabinet IR WAV file.
55 ///
56 /// Checks performed in order:
57 ///
58 /// 1. **Extension** – `file_name` must end with `.wav` (case-insensitive).
59 /// 2. **Parse** – bytes must form a valid WAV container readable by [`hound`].
60 /// Proprietary extensions that produce unexpected fmt-chunk sizes are
61 /// detected and surface a user-friendly re-export suggestion.
62 /// 3. **Impulse presence** – at least one of the first `256` samples must
63 /// exceed `impulse_threshold` in absolute value. A completely silent
64 /// header region usually indicates the wrong file was selected (e.g. a
65 /// music recording rather than an IR capture).
66 ///
67 /// Returns `Ok(())` when all checks pass, or `Err(description)` on the first
68 /// failing check.
69 fn validate_ir_wav_bytes(
70 &self,
71 file_name: &str,
72 file_bytes: &[u8],
73 impulse_threshold: f32,
74 ) -> Result<(), String>;
75}