浏览代码

feat(tauri): add plugin `initialize` (with config) API, run in parallel (#1194)

Lucas Fernandes Nogueira 4 年之前
父节点
当前提交
2058cc39c9
共有 4 个文件被更改,包括 99 次插入34 次删除
  1. 5 0
      .changes/plugin-config.md
  2. 13 7
      tauri-utils/src/config.rs
  3. 7 8
      tauri/src/app/runner.rs
  4. 74 19
      tauri/src/plugin.rs

+ 5 - 0
.changes/plugin-config.md

@@ -0,0 +1,5 @@
+---
+"tauri": minor
+---
+
+Plugins are now configurable through a `tauri.conf.json > "plugins" > $pluginName` object.

+ 13 - 7
tauri-utils/src/config.rs

@@ -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.
@@ -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())
   }
 }
 

+ 7 - 8
tauri/src/app/runner.rs

@@ -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)?;
 
@@ -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);
             }

+ 74 - 19
tauri/src/plugin.rs

@@ -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) {}
@@ -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)
   }
 }
 
@@ -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
 }
 
@@ -58,9 +112,11 @@ 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>(
@@ -68,29 +124,28 @@ pub(crate) async fn ready<D: ApplicationDispatcherExt + 'static>(
   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)