use std::path::Path;
use tauri::{
plugin::{Builder as PluginBuilder, TauriPlugin},
Manager, Runtime,
};
use tauri_specta::{collect_commands, Builder as SpectaBuilder, ErrorHandlingMode};
mod devices;
#[cfg(target_os = "linux")]
mod pipewire;
mod stream;
pub fn specta_builder<R: Runtime>() -> SpectaBuilder<R> {
SpectaBuilder::<R>::new()
.plugin_name("audio")
.commands(collect_commands![
devices::get_devices,
stream::create_stream,
stream::stop_stream
])
}
pub fn public_api_builder<R: Runtime>() -> SpectaBuilder<R> {
specta_builder().error_handling(ErrorHandlingMode::Throw)
}
pub fn export_public_api_bindings<R: Runtime>(
path: impl AsRef<Path>,
) -> Result<(), specta_typescript::Error> {
public_api_builder::<R>().export(specta_typescript::Typescript::default(), path.as_ref())
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
PluginBuilder::new("audio")
.setup(|app, _api| {
app.manage(stream::ActiveInputStream::default());
Ok(())
})
.invoke_handler(tauri::generate_handler![
devices::get_devices,
stream::create_stream,
stream::stop_stream
])
.build()
}
#[cfg(test)]
mod tests {
use std::path::Path;
use super::{export_public_api_bindings, specta_builder};
#[test]
fn exports_plugin_prefixed_typescript_bindings() {
let output_path = std::env::temp_dir().join("tauri-plugin-audio-bindings-test.ts");
specta_builder::<tauri::Wry>()
.export(specta_typescript::Typescript::default(), &output_path)
.expect("typescript binding export should succeed");
let output = std::fs::read_to_string(&output_path).expect("bindings should be readable");
assert!(output.contains(r#"__TAURI_INVOKE("plugin:audio|get_devices")"#));
assert!(output.contains(r#"__TAURI_INVOKE("plugin:audio|create_stream""#));
assert!(output.contains(r#"__TAURI_INVOKE("plugin:audio|stop_stream")"#));
let _ = std::fs::remove_file(output_path);
}
#[test]
fn public_guest_api_matches_generated_bindings() {
let output = generated_public_api("public-guest-api");
let guest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("guest-js/index.ts");
let checked_in =
std::fs::read_to_string(guest_path).expect("guest API bindings should be readable");
assert_eq!(
checked_in, output,
"run `pnpm --dir tauri-plugin-audio generate` to refresh guest-js/index.ts"
);
}
fn generated_public_api(test_name: &str) -> String {
let output_path = std::env::temp_dir().join(format!(
"tauri-plugin-audio-{test_name}-{}.ts",
std::process::id()
));
export_public_api_bindings::<tauri::Wry>(&output_path)
.expect("typescript public API export should succeed");
let output =
std::fs::read_to_string(&output_path).expect("public API bindings should be readable");
let _ = std::fs::remove_file(output_path);
output
}
}