mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-11-29 13:03:09 +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
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn build(self) -> App {
|
||||
App {
|
||||
|
@ -30,7 +30,7 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> {
|
||||
};
|
||||
|
||||
// build the webview
|
||||
let webview = build_webview(
|
||||
let mut webview = build_webview(
|
||||
application,
|
||||
main_content,
|
||||
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
|
||||
#[cfg(embedded_server)]
|
||||
spawn_server(server_url)?;
|
||||
@ -217,6 +219,15 @@ fn build_webview(
|
||||
"window-1"
|
||||
};
|
||||
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"}"# {
|
||||
let content_href = match content_clone {
|
||||
Content::Html(ref html) => html,
|
||||
@ -224,26 +235,43 @@ fn build_webview(
|
||||
};
|
||||
webview.eval(&format!(r#"window.location.href = "{}""#, content_href))?;
|
||||
} else {
|
||||
let handler_error;
|
||||
if let Err(tauri_handle_error) = crate::endpoints::handle(webview, arg) {
|
||||
let tauri_handle_error_str = tauri_handle_error.to_string();
|
||||
if tauri_handle_error_str.contains("unknown variant") {
|
||||
let handled_by_app = application.run_invoke_handler(webview, arg);
|
||||
handler_error = if let Err(e) = handled_by_app {
|
||||
Some(e.replace("'", "\\'"))
|
||||
} else {
|
||||
let handled = handled_by_app.expect("failed to check if the invoke was handled");
|
||||
if handled {
|
||||
None
|
||||
} else {
|
||||
Some(tauri_handle_error_str)
|
||||
let endpoint_handle = crate::endpoints::handle(webview, arg)
|
||||
.map_err(|tauri_handle_error| {
|
||||
let tauri_handle_error_str = tauri_handle_error.to_string();
|
||||
if tauri_handle_error_str.contains("unknown variant") {
|
||||
match application.run_invoke_handler(webview, arg) {
|
||||
Ok(handled) => {
|
||||
if handled {
|
||||
String::from("")
|
||||
} else {
|
||||
tauri_handle_error_str
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
};
|
||||
} else {
|
||||
handler_error = Some(tauri_handle_error_str);
|
||||
}
|
||||
|
||||
if let Some(handler_error_message) = handler_error {
|
||||
} else {
|
||||
tauri_handle_error_str
|
||||
}
|
||||
})
|
||||
.map_err(|app_handle_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))?;
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ pub mod cli;
|
||||
mod app;
|
||||
/// The Tauri API endpoints.
|
||||
mod endpoints;
|
||||
/// The plugin manager module contains helpers to manage runtime plugins.
|
||||
pub mod plugin;
|
||||
/// The salt helpers.
|
||||
mod salt;
|
||||
|
||||
@ -38,13 +40,13 @@ pub use anyhow::Result;
|
||||
pub use app::*;
|
||||
pub use tauri_api as api;
|
||||
pub use web_view::Handle;
|
||||
pub use web_view::WebView;
|
||||
|
||||
use std::process::Stdio;
|
||||
|
||||
use api::rpc::{format_callback, format_callback_result};
|
||||
use serde::Serialize;
|
||||
use threadpool::ThreadPool;
|
||||
use web_view::WebView;
|
||||
|
||||
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