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            // Refrescar el caché del plugin leyendo de disco y luego emitir el evento.
52            let app_for_async = app_handle.clone();
53            tauri::async_runtime::spawn(async move {
54                // Obtener el estado del ConfigManager y actualizar su cache.
55                let state = app_for_async.state::<desktop::ConfigManager<R>>();
56                if let Err(e) = state.inner().refresh_cache_from_file().await {
57                    eprintln!(
58                        "[Config Watcher Callback] Failed to refresh config cache: {}",
59                        e
60                    );
61                }
62                // Emitir evento para frontends
63                app_for_async.emit("config-changed", ()).unwrap_or_else(|e| {
64                    eprintln!(
65                        "[Config Watcher Callback] Failed to emit config-changed event: {}",
66                        e
67                    );
68                });
69            });
70        }
71    })
72}
73
74/// Initializes the plugin.
75pub fn init<R: Runtime>() -> TauriPlugin<R> {
76    Builder::new("config-manager")
77        .invoke_handler(tauri::generate_handler![
78            commands::read_config,
79            commands::write_config,
80            commands::set_darkmode
81        ])
82        .setup(|app, api| {
83            let config_manager = desktop::init(app, api)?;
84            let config_path = config_manager.config_path().to_path_buf();
85            app.manage(config_manager);
86
87            let app_handle_for_watcher = app.clone();
88            let event_handler = watch_config_file(&app_handle_for_watcher);
89
90            let mut watcher: RecommendedWatcher = notify::recommended_watcher(event_handler)
91                .map_err(|e| {
92                    Error::Other(format!("Cannot create watcher for config file: {}", e))
93                })?;
94
95            watcher
96                .watch(config_path.as_path(), notify::RecursiveMode::Recursive)
97                .map_err(|e| {
98                    Error::Other(format!(
99                        "Failed to watch config file {}: {}",
100                        config_path.display(),
101                        e
102                    ))
103                })?;
104
105            app.manage(Mutex::new(watcher));
106
107            Ok(())
108        })
109        .build()
110}