tauri_plugin_config_manager/
lib.rs

1use notify::{
2    event::{DataChange, ModifyKind},
3    EventKind, RecommendedWatcher, Watcher,
4};
5use std::sync::Mutex;
6use tauri::{
7    plugin::{Builder, TauriPlugin},
8    Emitter, Manager, Runtime,
9};
10
11mod commands;
12#[cfg(desktop)]
13mod desktop;
14mod error;
15
16pub use error::{Error, Result};
17
18#[cfg(desktop)]
19use desktop::ConfigManager;
20
21/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the config-manager APIs.
22pub trait ConfigManagerExt<R: Runtime> {
23    fn config_manager(&self) -> &ConfigManager<R>;
24}
25
26impl<R: Runtime, T: Manager<R>> crate::ConfigManagerExt<R> for T {
27    fn config_manager(&self) -> &ConfigManager<R> {
28        self.state::<ConfigManager<R>>().inner()
29    }
30}
31
32fn watch_config_file<R: Runtime + 'static>(
33    app: &tauri::AppHandle<R>,
34) -> Box<dyn FnMut(notify::Result<notify::Event>) + Send + 'static> {
35    let app_handle = app.clone();
36    Box::new(move |res: notify::Result<notify::Event>| {
37        let Ok(event) = res else {
38            if let Err(e) = res {
39                eprintln!(
40                    "[Config Watcher Callback] Error watching config file: {:?}",
41                    e
42                );
43            }
44            return;
45        };
46
47        if matches!(
48            event.kind,
49            EventKind::Modify(ModifyKind::Data(DataChange::Any))
50        ) {
51            app_handle.emit("config-changed", ()).unwrap_or_else(|e| {
52                eprintln!(
53                    "[Config Watcher Callback] Failed to emit config-changed event: {}",
54                    e
55                );
56            });
57        }
58    })
59}
60
61/// Initializes the plugin.
62pub fn init<R: Runtime>() -> TauriPlugin<R> {
63    Builder::new("config-manager")
64        .invoke_handler(tauri::generate_handler![
65            commands::read_config,
66            commands::write_config
67        ])
68        .setup(|app, api| {
69            let config_manager = desktop::init(app, api)?;
70            let config_path = config_manager.config_path().to_path_buf();
71            app.manage(config_manager);
72
73            let app_handle_for_watcher = app.clone();
74            let event_handler = watch_config_file(&app_handle_for_watcher);
75
76            let mut watcher: RecommendedWatcher = notify::recommended_watcher(event_handler)
77                .map_err(|e| {
78                    Error::Other(format!("Cannot create watcher for config file: {}", e))
79                })?;
80
81            watcher
82                .watch(config_path.as_path(), notify::RecursiveMode::Recursive)
83                .map_err(|e| {
84                    Error::Other(format!(
85                        "Failed to watch config file {}: {}",
86                        config_path.display(),
87                        e
88                    ))
89                })?;
90
91            app.manage(Mutex::new(watcher));
92
93            Ok(())
94        })
95        .build()
96}