mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-01 19:44:39 +03:00
feat(tauri) add plugin system for rust (#494)
* feat(tauri) add extension system * chore(tauri) rename extension to plugin * chore(tauri) add plugin docs * chore(tauri) expose WebView type * chore(changes) add changefile * fix(tauri) clippy warns * fix(changes) format * fix(changes) typo
This commit is contained in:
parent
660a2d87d6
commit
78afee9725
5
.changes/plugin-system.md
Normal file
5
.changes/plugin-system.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"tauri": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Plugin system added. You can hook into the webview lifecycle (`created`, `ready`) and extend the API adding logic to the `invoke_handler` by implementing the `tauri::plugin::Plugin` trait.
|
@ -91,6 +91,12 @@ impl AppBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a plugin to the runtime.
|
||||||
|
pub fn plugin(self, plugin: impl crate::plugin::Plugin + 'static) -> Self {
|
||||||
|
crate::plugin::register(plugin);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the App.
|
/// Builds the App.
|
||||||
pub fn build(self) -> App {
|
pub fn build(self) -> App {
|
||||||
App {
|
App {
|
||||||
|
@ -30,7 +30,7 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// build the webview
|
// build the webview
|
||||||
let webview = build_webview(
|
let mut webview = build_webview(
|
||||||
application,
|
application,
|
||||||
main_content,
|
main_content,
|
||||||
if application.splashscreen_html().is_some() {
|
if application.splashscreen_html().is_some() {
|
||||||
@ -45,6 +45,8 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
crate::plugin::created(&mut webview);
|
||||||
|
|
||||||
// spawn the embedded server on our server url
|
// spawn the embedded server on our server url
|
||||||
#[cfg(embedded_server)]
|
#[cfg(embedded_server)]
|
||||||
spawn_server(server_url)?;
|
spawn_server(server_url)?;
|
||||||
@ -217,6 +219,15 @@ fn build_webview(
|
|||||||
"window-1"
|
"window-1"
|
||||||
};
|
};
|
||||||
application.run_setup(webview, source.to_string());
|
application.run_setup(webview, source.to_string());
|
||||||
|
if source == "window-1" {
|
||||||
|
let handle = webview.handle();
|
||||||
|
handle
|
||||||
|
.dispatch(|webview| {
|
||||||
|
crate::plugin::ready(webview);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.expect("failed to invoke ready hook");
|
||||||
|
}
|
||||||
} else if arg == r#"{"cmd":"closeSplashscreen"}"# {
|
} else if arg == r#"{"cmd":"closeSplashscreen"}"# {
|
||||||
let content_href = match content_clone {
|
let content_href = match content_clone {
|
||||||
Content::Html(ref html) => html,
|
Content::Html(ref html) => html,
|
||||||
@ -224,26 +235,43 @@ fn build_webview(
|
|||||||
};
|
};
|
||||||
webview.eval(&format!(r#"window.location.href = "{}""#, content_href))?;
|
webview.eval(&format!(r#"window.location.href = "{}""#, content_href))?;
|
||||||
} else {
|
} else {
|
||||||
let handler_error;
|
let endpoint_handle = crate::endpoints::handle(webview, arg)
|
||||||
if let Err(tauri_handle_error) = crate::endpoints::handle(webview, arg) {
|
.map_err(|tauri_handle_error| {
|
||||||
let tauri_handle_error_str = tauri_handle_error.to_string();
|
let tauri_handle_error_str = tauri_handle_error.to_string();
|
||||||
if tauri_handle_error_str.contains("unknown variant") {
|
if tauri_handle_error_str.contains("unknown variant") {
|
||||||
let handled_by_app = application.run_invoke_handler(webview, arg);
|
match application.run_invoke_handler(webview, arg) {
|
||||||
handler_error = if let Err(e) = handled_by_app {
|
Ok(handled) => {
|
||||||
Some(e.replace("'", "\\'"))
|
if handled {
|
||||||
} else {
|
String::from("")
|
||||||
let handled = handled_by_app.expect("failed to check if the invoke was handled");
|
} else {
|
||||||
if handled {
|
tauri_handle_error_str
|
||||||
None
|
}
|
||||||
} else {
|
}
|
||||||
Some(tauri_handle_error_str)
|
Err(e) => e,
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
} else {
|
tauri_handle_error_str
|
||||||
handler_error = Some(tauri_handle_error_str);
|
}
|
||||||
}
|
})
|
||||||
|
.map_err(|app_handle_error| {
|
||||||
if let Some(handler_error_message) = handler_error {
|
if app_handle_error.contains("unknown variant") {
|
||||||
|
match crate::plugin::extend_api(webview, arg) {
|
||||||
|
Ok(handled) => {
|
||||||
|
if handled {
|
||||||
|
String::from("")
|
||||||
|
} else {
|
||||||
|
app_handle_error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => e,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app_handle_error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| e.replace("'", "\\'"));
|
||||||
|
if let Err(handler_error_message) = endpoint_handle {
|
||||||
|
if handler_error_message != "" {
|
||||||
webview.eval(&get_api_error_message(arg, handler_error_message))?;
|
webview.eval(&get_api_error_message(arg, handler_error_message))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ pub mod cli;
|
|||||||
mod app;
|
mod app;
|
||||||
/// The Tauri API endpoints.
|
/// The Tauri API endpoints.
|
||||||
mod endpoints;
|
mod endpoints;
|
||||||
|
/// The plugin manager module contains helpers to manage runtime plugins.
|
||||||
|
pub mod plugin;
|
||||||
/// The salt helpers.
|
/// The salt helpers.
|
||||||
mod salt;
|
mod salt;
|
||||||
|
|
||||||
@ -38,13 +40,13 @@ pub use anyhow::Result;
|
|||||||
pub use app::*;
|
pub use app::*;
|
||||||
pub use tauri_api as api;
|
pub use tauri_api as api;
|
||||||
pub use web_view::Handle;
|
pub use web_view::Handle;
|
||||||
|
pub use web_view::WebView;
|
||||||
|
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
use api::rpc::{format_callback, format_callback_result};
|
use api::rpc::{format_callback, format_callback_result};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use threadpool::ThreadPool;
|
use threadpool::ThreadPool;
|
||||||
use web_view::WebView;
|
|
||||||
|
|
||||||
thread_local!(static POOL: ThreadPool = ThreadPool::new(4));
|
thread_local!(static POOL: ThreadPool = ThreadPool::new(4));
|
||||||
|
|
||||||
|
71
tauri/src/plugin.rs
Normal file
71
tauri/src/plugin.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use web_view::WebView;
|
||||||
|
|
||||||
|
/// The plugin interface.
|
||||||
|
pub trait Plugin {
|
||||||
|
/// Callback invoked when the webview is created.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn created(&self, webview: &mut WebView<'_, ()>) {}
|
||||||
|
|
||||||
|
/// Callback invoked when the webview is ready.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn ready(&self, webview: &mut WebView<'_, ()>) {}
|
||||||
|
|
||||||
|
/// Add invoke_handler API extension commands.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn extend_api(&self, webview: &mut WebView<'_, ()>, payload: &str) -> Result<bool, String> {
|
||||||
|
Err("unknown variant".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local!(static PLUGINS: Arc<Mutex<Vec<Box<dyn Plugin>>>> = Default::default());
|
||||||
|
|
||||||
|
/// Registers a plugin.
|
||||||
|
pub fn register(ext: impl Plugin + 'static) {
|
||||||
|
PLUGINS.with(|plugins| {
|
||||||
|
let mut exts = plugins.lock().unwrap();
|
||||||
|
exts.push(Box::new(ext));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_plugin<T: FnMut(&Box<dyn Plugin>)>(mut callback: T) {
|
||||||
|
PLUGINS.with(|plugins| {
|
||||||
|
let exts = plugins.lock().unwrap();
|
||||||
|
for ext in exts.iter() {
|
||||||
|
callback(ext);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn created(webview: &mut WebView<'_, ()>) {
|
||||||
|
run_plugin(|ext| {
|
||||||
|
ext.created(webview);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn ready(webview: &mut WebView<'_, ()>) {
|
||||||
|
run_plugin(|ext| {
|
||||||
|
ext.ready(webview);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn extend_api(webview: &mut WebView<'_, ()>, arg: &str) -> Result<bool, String> {
|
||||||
|
PLUGINS.with(|plugins| {
|
||||||
|
let exts = plugins.lock().unwrap();
|
||||||
|
for ext in exts.iter() {
|
||||||
|
match ext.extend_api(webview, arg) {
|
||||||
|
Ok(handled) => {
|
||||||
|
if handled {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if !e.contains("unknown variant") {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user