Skip to content

Commit

Permalink
feat(tauri) add plugin system for rust (#494)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
lucasfernog authored Jul 12, 2020
1 parent 660a2d8 commit 78afee9
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changes/plugin-system.md
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 6 additions & 0 deletions tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
66 changes: 47 additions & 19 deletions tauri/src/app/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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)?;
Expand Down Expand Up @@ -217,33 +219,59 @@ 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,
Content::Url(ref url) => url,
};
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("'", "\\'"))
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 {
let handled = handled_by_app.expect("failed to check if the invoke was handled");
if handled {
None
} else {
Some(tauri_handle_error_str)
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 {
handler_error = Some(tauri_handle_error_str);
}

if let Some(handler_error_message) = handler_error {
} 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))?;
}
}
Expand Down
4 changes: 3 additions & 1 deletion tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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));

Expand Down
71 changes: 71 additions & 0 deletions tauri/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -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)
})
}

0 comments on commit 78afee9

Please sign in to comment.