#![warn(missing_docs, rust_2018_idioms)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#[cfg(target_os = "macos")]
#[doc(hidden)]
pub use embed_plist;
pub use error::Error;
#[cfg(shell_scope)]
#[doc(hidden)]
pub use regex;
pub use tauri_macros::{command, generate_handler};
pub mod api;
pub(crate) mod app;
pub mod async_runtime;
pub mod command;
mod endpoints;
mod error;
mod event;
mod hooks;
mod manager;
mod pattern;
pub mod plugin;
pub mod window;
pub use tauri_runtime as runtime;
pub mod scope;
pub mod settings;
mod state;
#[cfg(feature = "updater")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "updater")))]
pub mod updater;
pub use tauri_utils as utils;
#[cfg(feature = "wry")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
pub use tauri_runtime_wry::Wry;
pub type Result<T> = std::result::Result<T, Error>;
pub type SyncTask = Box<dyn FnOnce() + Send>;
use crate::runtime::window::PendingWindow;
use serde::Serialize;
use std::{collections::HashMap, fmt, sync::Arc};
pub use runtime::http;
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub use runtime::{menu::NativeImage, ActivationPolicy};
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
pub use {
self::app::tray::{SystemTrayEvent, SystemTrayHandle},
self::runtime::{
menu::{SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu},
SystemTray,
},
};
pub use {
self::app::WindowMenuEvent,
self::event::{Event, EventHandler},
self::runtime::menu::{CustomMenuItem, Menu, MenuEntry, MenuItem, Submenu},
self::window::menu::MenuEvent,
};
pub use {
self::app::{
App, AppHandle, AssetResolver, Builder, CloseRequestApi, GlobalWindowEvent, PathResolver,
RunEvent,
},
self::hooks::{
Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokePayload, InvokeResolver,
InvokeResponder, InvokeResponse, OnPageLoad, PageLoadPayload, SetupHook,
},
self::manager::Asset,
self::runtime::{
webview::{WebviewAttributes, WindowBuilder},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
WindowEvent,
},
ClipboardManager, GlobalShortcutManager, Icon, RunIteration, Runtime, UserAttentionType,
},
self::state::{State, StateManager},
self::utils::{
assets::Assets,
config::{Config, WindowUrl},
Env, PackageInfo,
},
self::window::{Monitor, Window},
scope::*,
};
pub use tauri_macros::generate_context;
#[macro_export]
macro_rules! tauri_build_context {
() => {
include!(concat!(env!("OUT_DIR"), "/tauri-build-context.rs"))
};
}
pub use pattern::Pattern;
pub struct Context<A: Assets> {
pub(crate) config: Config,
pub(crate) assets: Arc<A>,
pub(crate) default_window_icon: Option<Vec<u8>>,
pub(crate) system_tray_icon: Option<Icon>,
pub(crate) package_info: PackageInfo,
pub(crate) _info_plist: (),
pub(crate) pattern: Pattern,
#[cfg(shell_scope)]
pub(crate) shell_scope: scope::ShellScopeConfig,
}
impl<A: Assets> fmt::Debug for Context<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Context");
d.field("config", &self.config)
.field("default_window_icon", &self.default_window_icon)
.field("system_tray_icon", &self.system_tray_icon)
.field("package_info", &self.package_info)
.field("pattern", &self.pattern);
#[cfg(shell_scope)]
d.field("shell_scope", &self.shell_scope);
d.finish()
}
}
impl<A: Assets> Context<A> {
#[inline(always)]
pub fn config(&self) -> &Config {
&self.config
}
#[inline(always)]
pub fn config_mut(&mut self) -> &mut Config {
&mut self.config
}
#[inline(always)]
pub fn assets(&self) -> Arc<A> {
self.assets.clone()
}
#[inline(always)]
pub fn assets_mut(&mut self) -> &mut Arc<A> {
&mut self.assets
}
#[inline(always)]
pub fn default_window_icon(&self) -> Option<&[u8]> {
self.default_window_icon.as_deref()
}
#[inline(always)]
pub fn default_window_icon_mut(&mut self) -> &mut Option<Vec<u8>> {
&mut self.default_window_icon
}
#[inline(always)]
pub fn system_tray_icon(&self) -> Option<&Icon> {
self.system_tray_icon.as_ref()
}
#[inline(always)]
pub fn system_tray_icon_mut(&mut self) -> &mut Option<Icon> {
&mut self.system_tray_icon
}
#[inline(always)]
pub fn package_info(&self) -> &PackageInfo {
&self.package_info
}
#[inline(always)]
pub fn package_info_mut(&mut self) -> &mut PackageInfo {
&mut self.package_info
}
#[inline(always)]
pub fn pattern(&self) -> &Pattern {
&self.pattern
}
#[cfg(shell_scope)]
#[inline(always)]
pub fn allowed_commands(&self) -> &scope::ShellScopeConfig {
&self.shell_scope
}
#[inline(always)]
#[allow(clippy::too_many_arguments)]
pub fn new(
config: Config,
assets: Arc<A>,
default_window_icon: Option<Vec<u8>>,
system_tray_icon: Option<Icon>,
package_info: PackageInfo,
info_plist: (),
pattern: Pattern,
#[cfg(shell_scope)] shell_scope: scope::ShellScopeConfig,
) -> Self {
Self {
config,
assets,
default_window_icon,
system_tray_icon,
package_info,
_info_plist: info_plist,
pattern,
#[cfg(shell_scope)]
shell_scope,
}
}
}
pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
fn config(&self) -> Arc<Config> {
self.manager().config()
}
fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager().emit_filter(event, None, payload, |_| true)
}
fn emit_to<S: Serialize + Clone>(&self, label: &str, event: &str, payload: S) -> Result<()> {
self
.manager()
.emit_filter(event, None, payload, |w| label == w.label())
}
fn listen_global<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
self.manager().listen(event.into(), None, handler)
}
fn once_global<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: FnOnce(Event) + Send + 'static,
{
self.manager().once(event.into(), None, handler)
}
fn trigger_global(&self, event: &str, data: Option<String>) {
self.manager().trigger(event, None, data)
}
fn unlisten(&self, handler_id: EventHandler) {
self.manager().unlisten(handler_id)
}
fn get_window(&self, label: &str) -> Option<Window<R>> {
self.manager().get_window(label)
}
fn windows(&self) -> HashMap<String, Window<R>> {
self.manager().windows()
}
fn manage<T>(&self, state: T)
where
T: Send + Sync + 'static,
{
self.manager().state().set(state);
}
fn state<T>(&self) -> State<'_, T>
where
T: Send + Sync + 'static,
{
self
.manager()
.inner
.state
.try_get()
.expect("state() called before manage() for given type")
}
fn try_state<T>(&self) -> Option<State<'_, T>>
where
T: Send + Sync + 'static,
{
self.manager().inner.state.try_get()
}
fn env(&self) -> Env {
self.state::<Env>().inner().clone()
}
fn fs_scope(&self) -> FsScope {
self.state::<Scopes>().inner().fs.clone()
}
#[cfg(protocol_asset)]
fn asset_protocol_scope(&self) -> FsScope {
self.state::<Scopes>().inner().asset_protocol.clone()
}
#[cfg(shell_scope)]
fn shell_scope(&self) -> ShellScope {
self.state::<Scopes>().inner().shell.clone()
}
}
pub(crate) mod sealed {
use crate::{app::AppHandle, manager::WindowManager};
use tauri_runtime::{Runtime, RuntimeHandle};
pub enum RuntimeOrDispatch<'r, R: Runtime> {
Runtime(&'r R),
RuntimeHandle(R::Handle),
Dispatch(R::Dispatcher),
}
#[derive(Clone, serde::Serialize)]
struct WindowCreatedEvent {
label: String,
}
pub trait ManagerBase<R: Runtime> {
fn manager(&self) -> &WindowManager<R>;
fn runtime(&self) -> RuntimeOrDispatch<'_, R>;
fn app_handle(&self) -> AppHandle<R>;
fn create_new_window(
&self,
pending: crate::PendingWindow<R>,
) -> crate::Result<crate::Window<R>> {
use crate::runtime::Dispatch;
let labels = self.manager().labels().into_iter().collect::<Vec<_>>();
let pending = self
.manager()
.prepare_window(self.app_handle(), pending, &labels)?;
let window = match self.runtime() {
RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending),
RuntimeOrDispatch::RuntimeHandle(handle) => handle.create_window(pending),
RuntimeOrDispatch::Dispatch(mut dispatcher) => dispatcher.create_window(pending),
}
.map(|window| self.manager().attach_window(self.app_handle(), window))?;
self.manager().emit_filter(
"tauri://window-created",
None,
Some(WindowCreatedEvent {
label: window.label().into(),
}),
|w| w != &window,
)?;
Ok(window)
}
}
}
#[cfg(test)]
pub mod test;
#[cfg(test)]
mod test_utils {
use proptest::prelude::*;
pub fn assert_send<T: Send>() {}
pub fn assert_sync<T: Sync>() {}
#[allow(dead_code)]
pub fn assert_not_allowlist_error<T>(res: anyhow::Result<T>) {
if let Err(e) = res {
assert!(!e.to_string().contains("not on the allowlist"));
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(10000))]
#[test]
fn check_spawn_task(task in "[a-z]+") {
let dummy_task = async move {
format!("{}-run-dummy-task", task);
};
crate::async_runtime::spawn(dummy_task);
}
}
}