]> uap-core.de Git - note.git/commitdiff
add fs_notify main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 19 Jul 2026 14:44:29 +0000 (16:44 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 19 Jul 2026 14:44:29 +0000 (16:44 +0200)
Cargo.lock
application/backend/Cargo.toml
application/backend/src/backend.rs
application/backend/src/lib.rs
application/backend/src/notify.rs [new file with mode: 0644]

index d7e56a91250ad32504e6f9703d27e88286239cf4..d63a028e6e5e188945ff42b7a92371b10f91bbb2 100644 (file)
@@ -327,6 +327,7 @@ dependencies = [
  "dav-rs",
  "entity",
  "migration",
+ "notify",
  "rand 0.10.2",
  "sea-orm",
  "tokio",
@@ -1274,6 +1275,26 @@ dependencies = [
  "syn 2.0.117",
 ]
 
+[[package]]
+name = "inotify"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8"
+dependencies = [
+ "bitflags",
+ "inotify-sys",
+ "libc",
+]
+
+[[package]]
+name = "inotify-sys"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "is_terminal_polyfill"
 version = "1.70.2"
@@ -1307,6 +1328,26 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "kqueue"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5"
+dependencies = [
+ "kqueue-sys",
+ "libc",
+]
+
+[[package]]
+name = "kqueue-sys"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
+dependencies = [
+ "bitflags",
+ "libc",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.5.0"
@@ -1495,6 +1536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
 dependencies = [
  "libc",
+ "log",
  "wasi",
  "windows-sys 0.61.2",
 ]
@@ -1540,6 +1582,35 @@ dependencies = [
  "ui-rs",
 ]
 
+[[package]]
+name = "notify"
+version = "9.0.0-rc.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b44b771d4dd781ef14c84078693e67495da6b47f609f72e8a4da8420a861240e"
+dependencies = [
+ "bitflags",
+ "inotify",
+ "kqueue",
+ "libc",
+ "log",
+ "mio",
+ "notify-types",
+ "objc2-core-foundation",
+ "objc2-core-services",
+ "walkdir",
+ "windows-sys 0.61.2",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "notify-types"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "num-bigint"
 version = "0.4.6"
@@ -1611,6 +1682,25 @@ dependencies = [
  "libm",
 ]
 
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "objc2-core-services"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583300ad934cba24ff5292aee751ecc070f7ca6b39a574cc21b7b5e588e06a0b"
+dependencies = [
+ "libc",
+ "objc2-core-foundation",
+]
+
 [[package]]
 name = "once_cell"
 version = "1.21.4"
@@ -2128,6 +2218,15 @@ version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
 
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "schannel"
 version = "0.1.29"
@@ -2793,7 +2892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
 dependencies = [
  "fastrand",
- "getrandom 0.3.4",
+ "getrandom 0.4.3",
  "once_cell",
  "rustix",
  "windows-sys 0.61.2",
@@ -3110,6 +3209,16 @@ version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
 [[package]]
 name = "wasi"
 version = "0.11.1+wasi-snapshot-preview1"
@@ -3203,6 +3312,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
@@ -3373,6 +3491,12 @@ dependencies = [
  "tap",
 ]
 
+[[package]]
+name = "xxhash-rust"
+version = "0.8.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985eec839aaf2a1270af8f4ebcf63cf9401cfd90f0902f97c28d9f104ffbde72"
+
 [[package]]
 name = "yansi"
 version = "1.0.1"
index 3dcc0546e77c062d686deb11b0fc33fb177310c5..b797265bf19de3f60e31371b56b0ddef557fda16 100644 (file)
@@ -9,4 +9,5 @@ entity = { path = "../../entity" }
 migration = { path = "../../migration" }
 sea-orm = { version = "2.0.0-rc", features = [ "sqlx-sqlite", "sqlx-postgres", "runtime-tokio-native-tls", "macros" ] }
 tokio = { version = "1.52.1", features = ["rt-multi-thread"] }
-rand = "0.10.2"
\ No newline at end of file
+rand = "0.10.2"
+notify = "9.0.0-rc.4"
\ No newline at end of file
index d7aec6215e640be9e47ecc60eece515b3ebfc8cb..43663309e4104f456c08bebc664d5c376acf56e6 100644 (file)
@@ -35,6 +35,7 @@ use tokio::runtime::Runtime;
 use std::sync::{Arc};
 use std::thread::JoinHandle;
 use std::time::SystemTime;
+use notify::Event;
 use tokio::fs;
 use tokio::sync::{broadcast, mpsc};
 use tokio::sync::broadcast::error::SendError;
@@ -85,6 +86,7 @@ pub enum BroadcastMessage {
     NotebookStructureUpdate(Vec<Node>),
     NoteTitleUpdate(NoteTitleUpdate),
     NoteUpdate(NoteUpdate),
+    Notify(Event)
 }
 
 #[derive(Clone, PartialEq, Eq)]
index 4eed8cd63908bc154dbfcb2fbfc3856bdedaebc7..862d97d7972f004c426c9a6a703468f0f7759608 100644 (file)
@@ -1,4 +1,5 @@
 pub mod backend;
 pub mod lockmanager;
 pub mod storage;
-mod note;
\ No newline at end of file
+mod note;
+mod notify;
\ No newline at end of file
diff --git a/application/backend/src/notify.rs b/application/backend/src/notify.rs
new file mode 100644 (file)
index 0000000..cb9fba6
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+use std::collections::HashMap;
+use std::path::PathBuf;
+use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
+use tokio::sync::mpsc;
+use tokio::sync::mpsc::Receiver;
+use crate::backend::BroadcastMessage;
+use std::collections::hash_map::Entry;
+
+pub enum FSWatch {
+    Add(PathBuf),
+    Remove(PathBuf),
+}
+
+pub async fn fs_notify(mut rx: Receiver<FSWatch>, btx: tokio::sync::broadcast::Sender<BroadcastMessage>) -> notify::Result<()> {
+    let mut path_map = HashMap::<PathBuf, u32>::new();
+
+    let mut watcher = RecommendedWatcher::new(
+        move |res: Result<Event, notify::Error>| {
+            match res {
+                Ok(event) => {
+                    btx.send(BroadcastMessage::Notify(event));
+                },
+                Err(e) => {
+                    println!("Notify Error: {:?}", e);
+                }
+            }
+        },
+        Config::default(),
+    )?;
+
+    while let Some(fsw) = rx.recv().await {
+        match fsw {
+            FSWatch::Add(path) => {
+                match path_map.entry(path.clone()) {
+                    Entry::Occupied(mut entry) => {
+                        *entry.get_mut() += 1;
+                    }
+                    Entry::Vacant(entry) => {
+                        watcher.watch(&path, RecursiveMode::NonRecursive)?;
+                        entry.insert(1);
+                    }
+                }
+            }
+
+            FSWatch::Remove(path) => {
+                match path_map.entry(path.clone()) {
+                    Entry::Occupied(mut entry) => {
+                        let count = entry.get_mut();
+
+                        if *count > 1 {
+                            *count -= 1;
+                        } else {
+                            watcher.unwatch(&path)?;
+                            entry.remove();
+                        }
+                    }
+
+                    Entry::Vacant(_) => {
+                        // Remove without matching Add.
+                        // Ignore or log, depending on your needs.
+                        eprintln!("Attempted to remove unwatched path: {:?}", path);
+                    }
+                }
+            }
+        }
+    }
+
+
+    println!("fs_notify end");
+    Ok(())
+}
\ No newline at end of file