Skip to content

Commit

Permalink
feat(tauri): add plugin initialize (with config) API, run in parall…
Browse files Browse the repository at this point in the history
…el (#1194)
  • Loading branch information
lucasfernog committed Feb 10, 2021
1 parent e02c941 commit 2058cc3
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changes/plugin-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": minor
---

Plugins are now configurable through a `tauri.conf.json > "plugins" > $pluginName` object.
20 changes: 13 additions & 7 deletions tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,10 +390,8 @@ impl Default for BuildConfig {
}
}

type JsonObject = HashMap<String, JsonValue>;

/// The tauri.conf.json mapper.
#[derive(PartialEq, Deserialize, Debug)]
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
/// The Tauri configuration.
Expand All @@ -404,13 +402,21 @@ pub struct Config {
pub build: BuildConfig,
/// The plugins config.
#[serde(default)]
plugins: HashMap<String, JsonObject>,
pub plugins: PluginConfig,
}

impl Config {
/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
#[derive(Debug, Clone, Default, PartialEq, Deserialize)]
pub struct PluginConfig(HashMap<String, JsonValue>);

impl PluginConfig {
/// Gets a plugin configuration.
pub fn plugin_config<S: AsRef<str>>(&self, plugin_name: S) -> Option<&JsonObject> {
self.plugins.get(plugin_name.as_ref())
pub fn get<S: AsRef<str>>(&self, plugin_name: S) -> String {
self
.0
.get(plugin_name.as_ref())
.map(|config| config.to_string())
.unwrap_or_else(|| "{}".to_string())
}
}

Expand Down
15 changes: 7 additions & 8 deletions tauri/src/app/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ enum Content<T> {

/// Main entry point for running the Webview
pub(crate) fn run<A: ApplicationExt + 'static>(application: App<A>) -> crate::Result<()> {
let plugin_config = application.context.config.plugins.clone();
crate::async_runtime::block_on(async move {
crate::plugin::initialize(A::plugin_store(), plugin_config).await
})?;

// setup the content using the config struct depending on the compile target
let main_content = setup_content(&application.context)?;

Expand Down Expand Up @@ -344,14 +349,8 @@ fn build_webview<A: ApplicationExt + 'static>(
if app_handle_error.contains("unknown variant") {
let error =
match crate::plugin::extend_api(A::plugin_store(), &mut dispatcher, &arg).await {
Ok(handled) => {
if handled {
String::from("")
} else {
app_handle_error.to_string()
}
}
Err(e) => e,
Ok(_) => String::from(""),
Err(e) => e.to_string(),
};
endpoint_handle = Err(error);
}
Expand Down
93 changes: 74 additions & 19 deletions tauri/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,49 @@
use crate::api::config::PluginConfig;
use crate::async_runtime::Mutex;

use crate::ApplicationDispatcherExt;

use futures::future::join_all;

use std::sync::Arc;

/// The plugin error type.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Failed to serialize/deserialize.
#[error("JSON error: {0}")]
Json(serde_json::Error),
/// Unknown API type.
#[error("unknown API")]
UnknownApi,
}

impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
if error.to_string().contains("unknown variant") {
Self::UnknownApi
} else {
Self::Json(error)
}
}
}

/// The plugin interface.
#[async_trait::async_trait]
pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Sync {
/// The plugin name. Used as key on the plugin config object.
fn name(&self) -> &'static str;

/// Initialize the plugin.
#[allow(unused_variables)]
async fn initialize(&self, config: String) -> Result<(), Error> {
Ok(())
}

/// The JS script to evaluate on init.
async fn init_script(&self) -> Option<String> {
None
}

/// Callback invoked when the webview is created.
#[allow(unused_variables)]
async fn created(&self, dispatcher: D) {}
Expand All @@ -21,8 +54,8 @@ pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Sync {

/// Add invoke_handler API extension commands.
#[allow(unused_variables)]
async fn extend_api(&self, dispatcher: D, payload: &str) -> Result<bool, String> {
Err("unknown variant".to_string())
async fn extend_api(&self, dispatcher: D, payload: &str) -> Result<(), Error> {
Err(Error::UnknownApi)
}
}

Expand All @@ -38,18 +71,39 @@ pub async fn register<D: ApplicationDispatcherExt + 'static>(
plugins.push(Box::new(plugin));
}

pub(crate) async fn initialize<D: ApplicationDispatcherExt + 'static>(
store: &PluginStore<D>,
plugins_config: PluginConfig,
) -> crate::Result<()> {
let plugins = store.lock().await;
let mut futures = Vec::new();
for plugin in plugins.iter() {
let plugin_config = plugins_config.get(plugin.name());
futures.push(plugin.initialize(plugin_config));
}

for res in join_all(futures).await {
res?;
}

Ok(())
}

pub(crate) async fn init_script<D: ApplicationDispatcherExt + 'static>(
store: &PluginStore<D>,
) -> String {
let mut init = String::new();

let plugins = store.lock().await;
let mut futures = Vec::new();
for plugin in plugins.iter() {
if let Some(init_script) = plugin.init_script().await {
futures.push(plugin.init_script());
}

let mut init = String::new();
for res in join_all(futures).await {
if let Some(init_script) = res {
init.push_str(&format!("(function () {{ {} }})();", init_script));
}
}

init
}

Expand All @@ -58,39 +112,40 @@ pub(crate) async fn created<D: ApplicationDispatcherExt + 'static>(
dispatcher: &mut D,
) {
let plugins = store.lock().await;
let mut futures = Vec::new();
for plugin in plugins.iter() {
plugin.created(dispatcher.clone()).await;
futures.push(plugin.created(dispatcher.clone()));
}
join_all(futures).await;
}

pub(crate) async fn ready<D: ApplicationDispatcherExt + 'static>(
store: &PluginStore<D>,
dispatcher: &mut D,
) {
let plugins = store.lock().await;
let mut futures = Vec::new();
for plugin in plugins.iter() {
plugin.ready(dispatcher.clone()).await;
futures.push(plugin.ready(dispatcher.clone()));
}
join_all(futures).await;
}

pub(crate) async fn extend_api<D: ApplicationDispatcherExt + 'static>(
store: &PluginStore<D>,
dispatcher: &mut D,
arg: &str,
) -> Result<bool, String> {
) -> Result<bool, Error> {
let plugins = store.lock().await;
for ext in plugins.iter() {
match ext.extend_api(dispatcher.clone(), arg).await {
Ok(handled) => {
if handled {
return Ok(true);
}
}
Err(e) => {
if !e.contains("unknown variant") {
return Err(e);
}
Ok(_) => {
return Ok(true);
}
Err(e) => match e {
Error::UnknownApi => {}
_ => return Err(e),
},
}
}
Ok(false)
Expand Down

0 comments on commit 2058cc3

Please sign in to comment.