dx_forge/watcher_legacy/
mod.rs1pub mod cache_warmer;
2pub mod detector;
3pub mod lsp_detector;
4
5use anyhow::Result;
6use colored::Colorize;
7use sha2::{Digest, Sha256};
8use std::path::PathBuf;
9use std::sync::Arc;
10
11use crate::crdt::Operation;
12use crate::storage::{Database, OperationLog};
13use crate::sync::{remote::connect_peer, SyncManager};
14
15#[derive(Debug, Clone)]
17pub struct RapidChange {
18 pub path: String,
20 pub time_us: u64,
22 pub sequence: u64,
24}
25
26#[derive(Debug, Clone)]
28pub struct QualityChange {
29 pub path: String,
31 pub operations: Vec<Operation>,
33 pub time_us: u64,
35 pub total_us: u64,
37}
38
39#[derive(Debug, Clone)]
41pub enum ForgeEvent {
42 Rapid {
44 path: String,
45 time_us: u64,
46 sequence: u64,
47 },
48 Quality {
50 path: String,
51 operations: Vec<Operation>,
52 time_us: u64,
53 total_us: u64,
54 },
55}
56
57pub struct ForgeWatcher {
59 pub repo_root: PathBuf,
60 pub oplog: Arc<OperationLog>,
61 pub actor_id: String,
62 pub repo_id: String,
63 pub sync_mgr: Option<Arc<SyncManager>>,
64}
65
66impl ForgeWatcher {
67 pub async fn new<P: Into<PathBuf>>(
69 path: P,
70 enable_sync: bool,
71 peers: Vec<String>,
72 ) -> Result<Self> {
73 let path_buf = path.into();
74 let repo_root = path_buf.canonicalize().unwrap_or(path_buf);
75 let forge_dir = repo_root.join(".dx/forge");
76
77 let db = Database::new(&forge_dir)?;
78 db.initialize()?;
79 let oplog = Arc::new(OperationLog::new(Arc::new(db)));
80
81 let config_raw = tokio::fs::read_to_string(forge_dir.join("config.json")).await?;
83 let config: serde_json::Value = serde_json::from_str(&config_raw)?;
84 let actor_id = config["actor_id"].as_str().unwrap().to_string();
85 let repo_id = config["repo_id"]
86 .as_str()
87 .map(|s| s.to_string())
88 .unwrap_or_else(|| {
89 let mut hasher = Sha256::new();
90 let path_string = repo_root.to_string_lossy().into_owned();
91 hasher.update(path_string.as_bytes());
92 format!("local-{:x}", hasher.finalize())
93 });
94
95 let sync_mgr = if enable_sync {
96 Some(Arc::new(SyncManager::new()))
97 } else {
98 None
99 };
100
101 if let (Some(mgr), true) = (&sync_mgr, !peers.is_empty()) {
103 for url in peers {
104 let _ = connect_peer(
105 &url,
106 actor_id.clone(),
107 repo_id.clone(),
108 mgr.as_ref().clone(),
109 oplog.clone(),
110 )
111 .await;
112 }
113 }
114
115 let _ = tokio::task::spawn_blocking({
117 let repo_root_clone = repo_root.clone();
118 move || cache_warmer::warm_cache(&repo_root_clone)
119 })
120 .await??;
121
122 Ok(Self {
123 repo_root,
124 oplog,
125 actor_id,
126 repo_id,
127 sync_mgr,
128 })
129 }
130
131 pub async fn run(self) -> Result<()> {
133 let lsp_available = lsp_detector::detect_lsp_support().await?;
135
136 if lsp_available {
137 lsp_detector::start_lsp_monitoring(
139 self.repo_root,
140 self.oplog,
141 self.actor_id,
142 self.sync_mgr,
143 )
144 .await
145 } else {
146 tracing::info!(
148 "{} {} mode (no LSP extension detected)",
149 "👁️".bright_yellow(),
150 "File watching".bright_cyan().bold()
151 );
152 detector::start_watching(
153 self.repo_root,
154 self.oplog,
155 self.actor_id,
156 self.repo_id,
157 self.sync_mgr,
158 )
159 .await
160 }
161 }
162}
163
164pub async fn watch(path: PathBuf, enable_sync: bool, peers: Vec<String>) -> Result<()> {
166 let watcher = ForgeWatcher::new(path, enable_sync, peers).await?;
167 watcher.run().await
168}