plugin.rs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! Extend Tauri functionality.
  5. use crate::{api::config::PluginConfig, App, Invoke, PageLoadPayload, Params, Window};
  6. use serde_json::Value as JsonValue;
  7. use std::collections::HashMap;
  8. /// The plugin result type.
  9. pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
  10. /// The plugin interface.
  11. pub trait Plugin<P: Params>: Send {
  12. /// The plugin name. Used as key on the plugin config object.
  13. fn name(&self) -> &'static str;
  14. /// Initialize the plugin.
  15. #[allow(unused_variables)]
  16. fn initialize(&mut self, app: &App<P>, config: JsonValue) -> Result<()> {
  17. Ok(())
  18. }
  19. /// The JS script to evaluate on webview initialization.
  20. /// The script is wrapped into its own context with `(function () { /* your script here */ })();`,
  21. /// so global variables must be assigned to `window` instead of implicity declared.
  22. ///
  23. /// It's guaranteed that this script is executed before the page is loaded.
  24. fn initialization_script(&self) -> Option<String> {
  25. None
  26. }
  27. /// Callback invoked when the webview is created.
  28. #[allow(unused_variables)]
  29. fn created(&mut self, window: Window<P>) {}
  30. /// Callback invoked when the webview performs a navigation.
  31. #[allow(unused_variables)]
  32. fn on_page_load(&mut self, window: Window<P>, payload: PageLoadPayload) {}
  33. /// Add invoke_handler API extension commands.
  34. #[allow(unused_variables)]
  35. fn extend_api(&mut self, invoke: Invoke<P>) {}
  36. }
  37. crate::manager::default_args! {
  38. /// Plugin collection type.
  39. pub(crate) struct PluginStore<P: Params> {
  40. store: HashMap<&'static str, Box<dyn Plugin<P>>>,
  41. }
  42. }
  43. impl<P: Params> Default for PluginStore<P> {
  44. fn default() -> Self {
  45. Self {
  46. store: HashMap::new(),
  47. }
  48. }
  49. }
  50. impl<P: Params> PluginStore<P> {
  51. /// Adds a plugin to the store.
  52. ///
  53. /// Returns `true` if a plugin with the same name is already in the store.
  54. pub fn register<Plug: Plugin<P> + 'static>(&mut self, plugin: Plug) -> bool {
  55. self.store.insert(plugin.name(), Box::new(plugin)).is_some()
  56. }
  57. /// Initializes all plugins in the store.
  58. pub(crate) fn initialize(&mut self, app: &App<P>, config: &PluginConfig) -> crate::Result<()> {
  59. self.store.values_mut().try_for_each(|plugin| {
  60. plugin
  61. .initialize(
  62. app,
  63. config.0.get(plugin.name()).cloned().unwrap_or_default(),
  64. )
  65. .map_err(|e| crate::Error::PluginInitialization(plugin.name().to_string(), e.to_string()))
  66. })
  67. }
  68. /// Generates an initialization script from all plugins in the store.
  69. pub(crate) fn initialization_script(&self) -> String {
  70. self
  71. .store
  72. .values()
  73. .filter_map(|p| p.initialization_script())
  74. .fold(String::new(), |acc, script| {
  75. format!("{}\n(function () {{ {} }})();", acc, script)
  76. })
  77. }
  78. /// Runs the created hook for all plugins in the store.
  79. pub(crate) fn created(&mut self, window: Window<P>) {
  80. self
  81. .store
  82. .values_mut()
  83. .for_each(|plugin| plugin.created(window.clone()))
  84. }
  85. /// Runs the on_page_load hook for all plugins in the store.
  86. pub(crate) fn on_page_load(&mut self, window: Window<P>, payload: PageLoadPayload) {
  87. self
  88. .store
  89. .values_mut()
  90. .for_each(|plugin| plugin.on_page_load(window.clone(), payload.clone()))
  91. }
  92. pub(crate) fn extend_api(&mut self, mut invoke: Invoke<P>) {
  93. let command = invoke.message.command.replace("plugin:", "");
  94. let mut tokens = command.split('|');
  95. // safe to unwrap: split always has a least one item
  96. let target = tokens.next().unwrap();
  97. if let Some(plugin) = self.store.get_mut(target) {
  98. invoke.message.command = tokens
  99. .next()
  100. .map(|c| c.to_string())
  101. .unwrap_or_else(String::new);
  102. plugin.extend_api(invoke);
  103. } else {
  104. invoke
  105. .resolver
  106. .reject(format!("plugin {} not found", target));
  107. }
  108. }
  109. }