Skip to main content

truce_rack_core/
scanner.rs

1//! Plugin discovery trait.
2//!
3//! Each format wrapper exposes a `*Scanner` type that
4//! implements [`PluginScanner`]. Hosts may also implement
5//! their own composite scanners that aggregate results across
6//! formats (`MultiScanner { clap, vst3, au }` etc.) — there's
7//! nothing format-specific in the trait.
8
9use crate::error::Result;
10use crate::info::PluginInfo;
11use std::path::Path;
12
13/// Discover and load audio plugins of a single format.
14///
15/// # Thread safety
16///
17/// Scans are **off the audio thread**. They walk the filesystem,
18/// open dylibs, and can take seconds (a fresh AU scan touches
19/// 100+ plugins on a typical Mac). Hosts should never call
20/// `scan` from a real-time context — wrap in a worker thread
21/// if you need a non-blocking discovery flow.
22pub trait PluginScanner {
23    /// Concrete plugin type this scanner produces. Always
24    /// implements [`crate::Plugin`] for at least one sample
25    /// precision.
26    type Plugin;
27
28    /// Scan default OS plugin directories for this format.
29    /// Each format wrapper picks the conventional paths
30    /// (`~/Library/Audio/Plug-Ins/CLAP` and `/Library/...`
31    /// for CLAP on macOS, the registry on Windows, etc.).
32    ///
33    /// # Errors
34    /// I/O errors propagate from the directory walk; per-plugin
35    /// load failures are logged and skipped rather than
36    /// aborting the scan.
37    fn scan(&self) -> Result<Vec<PluginInfo>>;
38
39    /// Scan a specific directory. Useful for hosts that bundle
40    /// their own plugins or that want to test against a known
41    /// fixtures directory.
42    ///
43    /// # Errors
44    /// Same as [`PluginScanner::scan`].
45    fn scan_path(&self, path: &Path) -> Result<Vec<PluginInfo>>;
46
47    /// Materialise an instance from the [`PluginInfo`] returned
48    /// by `scan` / `scan_path`. Most wrappers actually dlopen
49    /// the plugin's dylib at this point; expect file I/O.
50    ///
51    /// # Errors
52    /// [`crate::Error::PluginNotFound`] when `info.unique_id`
53    /// doesn't match anything in this scanner's index;
54    /// [`crate::Error::LoadFailed`] on dylib / signature
55    /// errors.
56    fn load(&self, info: &PluginInfo) -> Result<Self::Plugin>;
57}