Skip to main content

braid_core/fs/
watcher.rs

1use crate::fs::mapping;
2use crate::fs::state::{Command, DaemonState};
3use crate::fs::sync::sync_local_to_remote;
4use notify::Event;
5
6pub async fn handle_fs_event(event: Event, state: DaemonState) {
7    if event.kind.is_access() || event.kind.is_other() {
8        return;
9    }
10    tracing::debug!("[BraidFS-WATCHER] Event: {:?}", event);
11
12    for path in event.paths {
13        // We handle both modifications and removals
14        // We handle both modifications and removals
15        let is_removal = event.kind.is_remove() || !path.exists();
16        let is_create = event.kind.is_create();
17
18        // Skip non-files if it's NOT a removal (e.g. it's a new directory)
19        if !is_removal && !path.is_file() {
20            tracing::info!("[BraidFS] Skipping non-file: {:?}", path);
21            continue;
22        }
23
24        // Skip if this is a dotfile or inside a hidden directory (like .braidfs) or a .tmp file
25        if path.components().any(|c| {
26            let s = c.as_os_str().to_string_lossy();
27            s.starts_with('.') || s.ends_with(".tmp")
28        }) {
29            continue;
30        }
31
32        // Skip if this was a pending write from us (to avoid echo loops)
33        if state.pending.should_ignore(&path) {
34            tracing::info!("[BraidFS] Skipping pending write: {:?}", path);
35            continue;
36        }
37
38        match mapping::path_to_url(&path) {
39            Ok(url) => {
40                tracing::info!("[BraidFS] File changed: {:?} -> {}", path, url);
41
42                // Auto-add new files to config.sync (IDE sync feature)
43                if is_create {
44                    let is_synced = {
45                        let cfg = state.config.read().await;
46                        cfg.sync.get(&url).copied().unwrap_or(false)
47                    };
48
49                    if !is_synced {
50                        tracing::info!("[BraidFS] Auto-adding new file to sync: {}", url);
51                        // Add to config.sync
52                        {
53                            let mut cfg = state.config.write().await;
54                            cfg.sync.insert(url.clone(), true);
55                            let _ = cfg.save().await;
56                        }
57                        // Send sync command to spawn subscription
58                        let _ = state.tx_cmd.send(Command::Sync { url: url.clone() }).await;
59                    }
60                }
61
62                // 3. Delegate to Debouncer for the actual sync
63                state.debouncer.request_sync(url, path).await;
64            }
65            Err(e) => {
66                tracing::debug!("[BraidFS] Ignoring file {:?}: {:?}", path, e);
67            }
68        }
69    }
70}