Skip to content

Commit 2058cc3

Browse files
authored
feat(tauri): add plugin initialize (with config) API, run in parallel (#1194)
1 parent e02c941 commit 2058cc3

File tree

4 files changed

+99
-34
lines changed

4 files changed

+99
-34
lines changed

.changes/plugin-config.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": minor
3+
---
4+
5+
Plugins are now configurable through a `tauri.conf.json > "plugins" > $pluginName` object.

tauri-utils/src/config.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,8 @@ impl Default for BuildConfig {
390390
}
391391
}
392392

393-
type JsonObject = HashMap<String, JsonValue>;
394-
395393
/// The tauri.conf.json mapper.
396-
#[derive(PartialEq, Deserialize, Debug)]
394+
#[derive(Debug, PartialEq, Deserialize)]
397395
#[serde(rename_all = "camelCase")]
398396
pub struct Config {
399397
/// The Tauri configuration.
@@ -404,13 +402,21 @@ pub struct Config {
404402
pub build: BuildConfig,
405403
/// The plugins config.
406404
#[serde(default)]
407-
plugins: HashMap<String, JsonObject>,
405+
pub plugins: PluginConfig,
408406
}
409407

410-
impl Config {
408+
/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
409+
#[derive(Debug, Clone, Default, PartialEq, Deserialize)]
410+
pub struct PluginConfig(HashMap<String, JsonValue>);
411+
412+
impl PluginConfig {
411413
/// Gets a plugin configuration.
412-
pub fn plugin_config<S: AsRef<str>>(&self, plugin_name: S) -> Option<&JsonObject> {
413-
self.plugins.get(plugin_name.as_ref())
414+
pub fn get<S: AsRef<str>>(&self, plugin_name: S) -> String {
415+
self
416+
.0
417+
.get(plugin_name.as_ref())
418+
.map(|config| config.to_string())
419+
.unwrap_or_else(|| "{}".to_string())
414420
}
415421
}
416422

tauri/src/app/runner.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ enum Content<T> {
1818

1919
/// Main entry point for running the Webview
2020
pub(crate) fn run<A: ApplicationExt + 'static>(application: App<A>) -> crate::Result<()> {
21+
let plugin_config = application.context.config.plugins.clone();
22+
crate::async_runtime::block_on(async move {
23+
crate::plugin::initialize(A::plugin_store(), plugin_config).await
24+
})?;
25+
2126
// setup the content using the config struct depending on the compile target
2227
let main_content = setup_content(&application.context)?;
2328

@@ -344,14 +349,8 @@ fn build_webview<A: ApplicationExt + 'static>(
344349
if app_handle_error.contains("unknown variant") {
345350
let error =
346351
match crate::plugin::extend_api(A::plugin_store(), &mut dispatcher, &arg).await {
347-
Ok(handled) => {
348-
if handled {
349-
String::from("")
350-
} else {
351-
app_handle_error.to_string()
352-
}
353-
}
354-
Err(e) => e,
352+
Ok(_) => String::from(""),
353+
Err(e) => e.to_string(),
355354
};
356355
endpoint_handle = Err(error);
357356
}

tauri/src/plugin.rs

+74-19
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,49 @@
1+
use crate::api::config::PluginConfig;
12
use crate::async_runtime::Mutex;
2-
33
use crate::ApplicationDispatcherExt;
44

5+
use futures::future::join_all;
6+
57
use std::sync::Arc;
68

9+
/// The plugin error type.
10+
#[derive(Debug, thiserror::Error)]
11+
pub enum Error {
12+
/// Failed to serialize/deserialize.
13+
#[error("JSON error: {0}")]
14+
Json(serde_json::Error),
15+
/// Unknown API type.
16+
#[error("unknown API")]
17+
UnknownApi,
18+
}
19+
20+
impl From<serde_json::Error> for Error {
21+
fn from(error: serde_json::Error) -> Self {
22+
if error.to_string().contains("unknown variant") {
23+
Self::UnknownApi
24+
} else {
25+
Self::Json(error)
26+
}
27+
}
28+
}
29+
730
/// The plugin interface.
831
#[async_trait::async_trait]
932
pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Sync {
33+
/// The plugin name. Used as key on the plugin config object.
34+
fn name(&self) -> &'static str;
35+
36+
/// Initialize the plugin.
37+
#[allow(unused_variables)]
38+
async fn initialize(&self, config: String) -> Result<(), Error> {
39+
Ok(())
40+
}
41+
1042
/// The JS script to evaluate on init.
1143
async fn init_script(&self) -> Option<String> {
1244
None
1345
}
46+
1447
/// Callback invoked when the webview is created.
1548
#[allow(unused_variables)]
1649
async fn created(&self, dispatcher: D) {}
@@ -21,8 +54,8 @@ pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Sync {
2154

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

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

74+
pub(crate) async fn initialize<D: ApplicationDispatcherExt + 'static>(
75+
store: &PluginStore<D>,
76+
plugins_config: PluginConfig,
77+
) -> crate::Result<()> {
78+
let plugins = store.lock().await;
79+
let mut futures = Vec::new();
80+
for plugin in plugins.iter() {
81+
let plugin_config = plugins_config.get(plugin.name());
82+
futures.push(plugin.initialize(plugin_config));
83+
}
84+
85+
for res in join_all(futures).await {
86+
res?;
87+
}
88+
89+
Ok(())
90+
}
91+
4192
pub(crate) async fn init_script<D: ApplicationDispatcherExt + 'static>(
4293
store: &PluginStore<D>,
4394
) -> String {
44-
let mut init = String::new();
45-
4695
let plugins = store.lock().await;
96+
let mut futures = Vec::new();
4797
for plugin in plugins.iter() {
48-
if let Some(init_script) = plugin.init_script().await {
98+
futures.push(plugin.init_script());
99+
}
100+
101+
let mut init = String::new();
102+
for res in join_all(futures).await {
103+
if let Some(init_script) = res {
49104
init.push_str(&format!("(function () {{ {} }})();", init_script));
50105
}
51106
}
52-
53107
init
54108
}
55109

@@ -58,39 +112,40 @@ pub(crate) async fn created<D: ApplicationDispatcherExt + 'static>(
58112
dispatcher: &mut D,
59113
) {
60114
let plugins = store.lock().await;
115+
let mut futures = Vec::new();
61116
for plugin in plugins.iter() {
62-
plugin.created(dispatcher.clone()).await;
117+
futures.push(plugin.created(dispatcher.clone()));
63118
}
119+
join_all(futures).await;
64120
}
65121

66122
pub(crate) async fn ready<D: ApplicationDispatcherExt + 'static>(
67123
store: &PluginStore<D>,
68124
dispatcher: &mut D,
69125
) {
70126
let plugins = store.lock().await;
127+
let mut futures = Vec::new();
71128
for plugin in plugins.iter() {
72-
plugin.ready(dispatcher.clone()).await;
129+
futures.push(plugin.ready(dispatcher.clone()));
73130
}
131+
join_all(futures).await;
74132
}
75133

76134
pub(crate) async fn extend_api<D: ApplicationDispatcherExt + 'static>(
77135
store: &PluginStore<D>,
78136
dispatcher: &mut D,
79137
arg: &str,
80-
) -> Result<bool, String> {
138+
) -> Result<bool, Error> {
81139
let plugins = store.lock().await;
82140
for ext in plugins.iter() {
83141
match ext.extend_api(dispatcher.clone(), arg).await {
84-
Ok(handled) => {
85-
if handled {
86-
return Ok(true);
87-
}
88-
}
89-
Err(e) => {
90-
if !e.contains("unknown variant") {
91-
return Err(e);
92-
}
142+
Ok(_) => {
143+
return Ok(true);
93144
}
145+
Err(e) => match e {
146+
Error::UnknownApi => {}
147+
_ => return Err(e),
148+
},
94149
}
95150
}
96151
Ok(false)

0 commit comments

Comments
 (0)