1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
//! Bindings to the [`Tauri API`](https://tauri.app/v1/api/js/) for projects using [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen)
//!
//! Tauri is a framework for building tiny, blazing fast, and secure cross-platform applications.
//! Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface.
//! The backend of the application is a rust binary, leveraging the [`tauri`] crate.
//!
//! This crate contains idiomatic rust bindings to the backend, for usage within Rust projects that target wasm32-unknown-unknown,
//! for example Rust frontend frameworks such as [`yew`](https://yew.rs), [`sycamore`](https://sycamore-rs.netlify.app) or [`dominator`](https://github.com/Pauan/rust-dominator).
//!
//! The wasmtime crate has similar concepts to the the JS WebAssembly API as well as the proposed C API, but the Rust API is designed for efficiency, ergonomics, and expressivity in Rust. As with all other Rust code you’re guaranteed that programs will be safe (not have undefined behavior or segfault) so long as you don’t use unsafe in your own program.
//!
//! # Differences to the JavaScript API
//!
//! ## Event Listeners
//!
//! Event Listeners, such as [`event::listen`] module or [`window::WebviewWindow::listen`],
//! are modeled as async streams of data using the [`futures::Stream`] trait instead of using callbacks.
//! Streams have multiple advantages over callbacks:
//!
//! #### Stream Combinators
//!
//! Streams are essentially the async equivalent of the standard [`Iterator`] and therefore expose a very similar set of combinator functions.
//! This means streams are much more versatile and ergonomic than simple callbacks.
//!
//! For example, we can use Stream combinators and various utility functions
//! to replicate the `registerAll` function that unregisters the shortcuts after 20 seconds:
//!
//! ```rust
//! use futures::{future, stream, Stream, StreamExt};
//! use std::time::Duration;
//! use tauri_sys::global_shortcut;
//!
//! async fn register_with_shortcut<'a>(
//! shortcut: &'a str,
//! ) -> anyhow::Result<impl Stream<Item = &'a str>> {
//! let stream = global_shortcut::register(shortcut).await?;
//!
//! Ok(stream.map(move |_| shortcut))
//! }
//!
//! async fn register_all() {
//! let shortcuts = ["CommandOrControl+Shift+C", "Ctrl+Alt+F12"];
//!
//! let timeout = gloo_timers::future::sleep(Duration::from_secs(20));
//!
//! // await the futures that creates the streams, exiting early if any future resolves with an error
//! let streams = future::try_join_all(shortcuts.map(register_with_shortcut)).await?;
//!
//! // combine all streams into one
//! let mut events = stream::select_all(streams).take_until(timeout);
//!
//! while let Some(shortcut) = events.next().await {
//! log::debug!("Shortcut {} triggered", shortcut);
//! }
//! }
//! ```
//!
//! #### Automatic cleanup
//!
//! Streams follow Rust's RAII idiom as they automatically clean up after themselves when being dropped.
//! No need to manually call `unlisten` like in the JS API to avoid memory leaks or double-listens.
//!
//! ```rust
//! async fn process_some_errors() {
//! let win = WebviewWindow::get_by_label("main").unwrap();
//!
//! let errors = win.listen("tauri://error").await?
//! .take(3);
//!
//! while let Some(err) = errors.next().await {
//! log::error!("Something bad happened! {}", err)
//! }
//!
//! // the stream is dropped here and the underlying listener automatically detached.
//! }
//! ```
//!
//! #### Streams are buffered
//!
//! Streams, much like iterators, are poll-based meaning the caller is responsible for advancing it.
//! This allows greater flexibility as you can freely decide *when* to process events.
//! Event streams are internally backed by an unbounded queue so events are buffered until read,
//! so no events are getting lost even if you temporarily pause processing.
//!
//! Being unbounded means the memory consumption will grow if the stream is kept around, but not read from.
//! This is rarely a concern in practice, but if you need to suspend processing of events for a long time,
//! you should rather drop the entire stream and re-create it as needed later.
//!
//! ### Cancelling Streams
//!
//! One usecase of the `unlisten` function might intuitively not map well to streams: Cancellation.
//! In JavaScript you can do this when you want to detach an event listener:
//!
//! ```js
//! import { listen } from '@tauri-apps/api/event'
//!
//! const unlisten = await listen('rust-event', (ev) => console.log(ev))
//!
//! // Some time later. We are no longer interested in listening to the event
//! unlisten()
//! ```
//!
//! But if the Rust event stream only gets detached when the stream get's dropped, how can we cancel the stream at will?
//! We can make use of the combinators and utility functions offered by the [`futures`] crate again, namely the [`futures::stream::Abortable`] type:
//!
//! ```rust
//! use tauri_sys::event::listen;
//!
//! let events = listen::<()>("rust-event").await?
//! // abort handle behaves identical to the JavaScript `unlisten` function
//! let (events, abort_handle) = futures::stream::abortable(events);
//!
//! while let Some(_) = events.next().await {
//! log::debug!("Received event!");
//! }
//!
//! // in some other task, when we're done with listening to the events
//! abort_handle.abort();
//! ```
#[cfg(feature = "app")]
pub mod app;
#[cfg(feature = "clipboard")]
pub mod clipboard;
#[cfg(feature = "dialog")]
pub mod dialog;
mod error;
#[cfg(feature = "event")]
pub mod event;
#[cfg(feature = "fs")]
pub mod fs;
#[cfg(feature = "global_shortcut")]
pub mod global_shortcut;
#[cfg(feature = "mocks")]
pub mod mocks;
#[cfg(feature = "notification")]
pub mod notification;
#[cfg(feature = "os")]
pub mod os;
#[cfg(feature = "path")]
pub mod path;
#[cfg(feature = "process")]
pub mod process;
#[cfg(feature = "tauri")]
pub mod tauri;
#[cfg(feature = "updater")]
pub mod updater;
#[cfg(feature = "window")]
pub mod window;
pub use error::Error;
pub(crate) type Result<T> = core::result::Result<T, Error>;
#[cfg(any(feature = "dialog", feature = "window"))]
pub(crate) mod utils {
pub struct ArrayIterator {
pos: u32,
arr: js_sys::Array,
}
impl ArrayIterator {
pub fn new(arr: js_sys::Array) -> Self {
Self { pos: 0, arr }
}
}
impl Iterator for ArrayIterator {
type Item = wasm_bindgen::JsValue;
fn next(&mut self) -> Option<Self::Item> {
let raw = self.arr.get(self.pos);
if raw.is_undefined() {
None
} else {
self.pos += 1;
Some(raw)
}
}
}
}