zng/
fs_watcher.rs

1#![cfg(feature = "fs_watcher")]
2
3//! File system watcher service and other types.
4//!
5//! The [`WATCHER`] service can be used to get notifications when a file or directory is modified. It also provides
6//! ways to bind a file to a variable, automatically synchronizing both.
7//!
8//! The example below binds the current content of a text file to at text variable using [`WATCHER.read`](WATCHER::read).
9//! Any external change made to the text file updates the UI text.
10//!
11//! ```
12//! use zng::{fs_watcher::WATCHER, prelude::*};
13//!
14//! # fn main() { }
15//! # fn demo() {
16//! # let _ =
17//! Text!(WATCHER.read("dump.log", Txt::from(""), |f| f.ok()?.text().ok()))
18//! # ; }
19//! ```
20//!
21//! The next example created a read-write binding with the text file, any external change made to the text file updates the
22//! `TextInput!` and any change made using the `TextInput!` updates the file contents.
23//!
24//! ```
25//! use zng::{fs_watcher::WATCHER, prelude::*};
26//!
27//! # fn main() { }
28//! # fn demo() {
29//! # let _ =
30//! TextInput!(zng::fs_watcher::WATCHER.sync(
31//!     "dump.log",
32//!     // initial value
33//!     Txt::from(""),
34//!     // read, only updates txt if returns Some
35//!     |f| f.ok()?.text().ok(),
36//!     // write, only change file if commit called.
37//!     |txt, f| {
38//!         if let Ok(mut f) = f {
39//!             if f.write_text(&txt).is_ok() {
40//!                 // replace actual file with temp that was successfully written.
41//!                 let _ = f.commit();
42//!             } else {
43//!                 f.cancel();
44//!             }
45//!         }
46//!     },
47//! ))
48//! # ; }
49//! ```
50//!
51//! The [`WATCHER`] service abstracts away most of the headache of interacting with the file system. This service
52//! is used internally by the implementations of [`CONFIG`] and [`L10N`].
53//!
54//! [`CONFIG`]: crate::config::CONFIG
55//! [`L10N`]: crate::l10n::L10N
56//!
57//! # Full API
58//!
59//! See [`zng_ext_fs_watcher`] for the full watcher API.
60
61pub use zng_ext_fs_watcher::{
62    FS_CHANGES_EVENT, FsChange, FsChangeNote, FsChangeNoteHandle, FsChangesArgs, WATCHER, WatchFile, WatcherHandle, WatcherReadStatus,
63    WatcherSyncStatus, WriteFile, fs_event,
64};
65
66#[cfg(feature = "image")]
67mod images_ext {
68    use std::path::PathBuf;
69
70    use zng_app::hn;
71    use zng_ext_fs_watcher::WATCHER;
72    use zng_ext_image::*;
73
74    /// File watcher extensions for [`IMAGES`] service.
75    #[expect(non_camel_case_types)]
76    pub trait IMAGES_Ext {
77        /// Like [`IMAGES.read`] with automatic reload when the file at `path` is modified.
78        ///
79        /// [`IMAGES.read`]: IMAGES::read
80        fn watch(&self, path: impl Into<PathBuf>) -> ImageVar;
81
82        /// Like [`IMAGES.image`] with automatic cache reload when the file at `path` is modified.
83        ///
84        /// [`IMAGES.image`]: IMAGES::image
85        fn watch_image(&self, path: impl Into<PathBuf>, options: ImageOptions, limits: Option<ImageLimits>) -> ImageVar;
86    }
87    impl IMAGES_Ext for IMAGES {
88        fn watch(&self, path: impl Into<PathBuf>) -> ImageVar {
89            watch(path.into())
90        }
91
92        fn watch_image(&self, path: impl Into<PathBuf>, options: ImageOptions, limits: Option<ImageLimits>) -> ImageVar {
93            watch_image(path.into(), options, limits)
94        }
95    }
96
97    fn watch(path: PathBuf) -> ImageVar {
98        let img = IMAGES.read(path.clone());
99        let handle = WATCHER.on_file_changed(
100            path.clone(),
101            true,
102            hn!(|_| {
103                let mut opt = ImageOptions::cache();
104                opt.cache_mode = ImageCacheMode::Reload;
105                let _ = IMAGES.image(path.clone(), opt, None);
106            }),
107        );
108        img.hold(handle).perm();
109        img
110    }
111
112    fn watch_image(path: PathBuf, option: ImageOptions, limits: Option<ImageLimits>) -> ImageVar {
113        let img = IMAGES.image(ImageSource::Read(path.clone()), option.clone(), limits.clone());
114        let handle = WATCHER.on_file_changed(
115            path.clone(),
116            true,
117            hn!(|_| {
118                let mut opt = option.clone();
119                opt.cache_mode = ImageCacheMode::Reload;
120                let _ = IMAGES.image(ImageSource::Read(path.clone()), opt, limits.clone());
121            }),
122        );
123        img.hold(handle).perm();
124        img
125    }
126}
127#[cfg(feature = "image")]
128pub use images_ext::*;