Skip to content

Commit 38f14e4

Browse files
committed
feat(daemon): ensure new projects server support
1 parent 697d99f commit 38f14e4

File tree

4 files changed

+148
-146
lines changed

4 files changed

+148
-146
lines changed

src/compile.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,91 @@ pub async fn ensure_server_config(root: &path::PathBuf) -> Result<()> {
178178

179179
Ok(())
180180
}
181+
182+
#[cfg(feature = "daemon")]
183+
pub async fn ensure_server_support<'a>(
184+
state: &'a mut tokio::sync::OwnedMutexGuard<crate::state::State>,
185+
name: &String,
186+
root: &std::path::PathBuf,
187+
path: Option<&std::path::PathBuf>,
188+
) -> Result<bool> {
189+
use crate::{error::XcodeGenError, xcodegen};
190+
use tokio::fs::metadata;
191+
let compile_exists = metadata(root.join(".compile")).await.is_ok();
192+
193+
if ensure_server_config(&root).await.is_err() {
194+
"fail to ensure build server configuration!"
195+
.pipe(|msg| state.clients.echo_err(&root, &name, msg))
196+
.await;
197+
}
198+
199+
if let Some(path) = path {
200+
let generated = match xcodegen::regenerate(path, &root).await {
201+
Ok(generated) => generated,
202+
Err(e) => {
203+
state.clients.echo_err(&root, &name, &e.to_string()).await;
204+
return Err(e);
205+
}
206+
};
207+
208+
if generated {
209+
state.projects.get_mut(root)?.update().await?
210+
}
211+
} else if compile_exists {
212+
return Ok(false);
213+
}
214+
215+
if xcodegen::is_valid(&root) && path.is_none() {
216+
"⚙ generating xcodeproj ..."
217+
.pipe(|msg| state.clients.echo_msg(&root, &name, msg))
218+
.await;
219+
220+
if let Some(err) = match xcodegen::generate(&root).await {
221+
Ok(status) => {
222+
if status.success() {
223+
"setup: ⚙ generate xcodeproj ..."
224+
.pipe(|msg| state.clients.echo_msg(&root, &name, msg))
225+
.await;
226+
None
227+
} else {
228+
Some(XcodeGenError::XcodeProjUpdate(name.into()).into())
229+
}
230+
}
231+
Err(e) => Some(e),
232+
} {
233+
let msg = format!("fail to generate xcodeproj: {err}");
234+
state.clients.echo_err(&root, &name, &msg).await;
235+
}
236+
};
237+
238+
// TODO(compile): check for .xcodeproj if project.yml is not generated
239+
if !compile_exists {
240+
"⚙ generating compile database .."
241+
.pipe(|msg| state.clients.echo_msg(&root, &name, msg))
242+
.await;
243+
}
244+
245+
// The following command won't successed if this file doesn't exists
246+
if let Err(err) = update_compilation_file(&root).await {
247+
"setup: fail to regenerate compilation database!"
248+
.pipe(|msg| state.clients.echo_err(&root, &name, msg))
249+
.await;
250+
251+
use crate::util::proc_exists;
252+
for (pid, client) in state.clients.iter() {
253+
if proc_exists(pid, || {}) {
254+
let mut logger = client.new_logger("Compile Error", name, &None);
255+
logger.set_running().await.ok();
256+
let ref win = Some(logger.open_win().await?);
257+
logger.log(err.to_string(), win).await.ok();
258+
logger.set_status_end(false, true).await.ok();
259+
}
260+
}
261+
262+
return Err(err);
263+
}
264+
265+
tracing::info!("Updated `{name}/.compile`");
266+
267+
Ok(true)
268+
}

src/daemon/requests/register.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use crate::types::Client;
2-
31
use super::*;
2+
use crate::types::Client;
43

54
/// Register new client with workspace
65
#[derive(Debug, Serialize, Deserialize)]
@@ -21,39 +20,52 @@ impl<'a> Requester<'a, Register> for Register {
2120
impl Handler for Register {
2221
async fn handle(self) -> Result<()> {
2322
use crate::constants::DAEMON_STATE;
23+
use tap::Pipe;
2424

25-
let Register { client, .. } = &self;
26-
let Client { root, pid } = &client;
25+
// NOTE: This logic should maybe moved to tokio::spawn to speed up initialization.
26+
let name = self.client.abbrev_root();
27+
tracing::info!("Register({}, {}): ", self.client.pid, name);
2728

28-
tracing::info!("Register({pid}, {}): ", client.abbrev_root());
29+
let mut state = DAEMON_STATE.clone().lock_owned().await;
2930

30-
let state = DAEMON_STATE.clone();
31-
let mut state = state.lock().await;
32-
33-
if let Ok(project) = state.projects.get_mut(root) {
31+
if let Ok(project) = state.projects.get_mut(&self.client.root) {
3432
// NOTE: Add client pid to project
35-
project.clients.push(*pid);
33+
project.clients.push(self.client.pid.clone());
3634
} else {
3735
// NOTE: Create nvim client
3836
state.projects.add(&self).await?;
3937

40-
let project = state.projects.get(root).unwrap();
38+
let project = state.projects.get(&self.client.root).unwrap();
4139
let ignore_patterns = project.ignore_patterns.clone();
4240

4341
// NOTE: Add watcher
4442
state
4543
.watcher
46-
.add_project_watcher(client, ignore_patterns)
44+
.add_project_watcher(&self.client, ignore_patterns)
4745
.await
4846
}
4947

5048
// NOTE: Create nvim client
5149
state.clients.add(&self).await?;
5250

53-
// NOTE: Sink Daemon to nvim vim.g.xbase.state
54-
let _update_handle = state.sync_client_state().await?;
51+
tokio::spawn(async move {
52+
let ref mut state = DAEMON_STATE.clone().lock_owned().await;
53+
54+
// NOTE: Ensure project is ready for xbase build server
55+
let generated =
56+
crate::compile::ensure_server_support(state, &name, &self.client.root, None)
57+
.await?;
58+
59+
if generated {
60+
"setup: ✅"
61+
.pipe(|msg| state.clients.echo_msg(&self.client.root, &name, msg))
62+
.await;
63+
}
5564

56-
// TODO(register): Ensure buildServer.json and .compile exists in a subprocess
65+
// NOTE: Sink Daemon to nvim vim.g.xbase
66+
state.sync_client_state().await?;
67+
anyhow::Ok(())
68+
});
5769

5870
Ok(())
5971
}

src/watcher/handle/project.rs

Lines changed: 22 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{is_seen, WatchArguments, WatchError};
2-
use crate::{compile, Error};
2+
use crate::compile;
33
use crate::{constants::DAEMON_STATE, types::Client};
44
use notify::event::{DataChange, ModifyKind};
55
use notify::{Event, EventKind};
@@ -20,168 +20,75 @@ pub async fn create(args: WatchArguments) -> Result<(), WatchError> {
2020
let Client { root, .. } = info.try_into_project()?;
2121

2222
if should_skip_compile(&event, &path, args.last_seen).await {
23-
tracing::trace!("Skipping {:#?}", &event);
2423
return Ok(());
2524
}
2625

27-
tracing::trace!("[NewEvent] {:#?}", &event);
28-
29-
let state = DAEMON_STATE.clone();
30-
let mut state = state.lock().await;
26+
let ref name = root.file_name().unwrap().to_string_lossy().to_string();
27+
let ref mut state = DAEMON_STATE.clone().lock_owned().await;
3128
let mut debounce = args.debounce.lock().await;
3229

33-
let project_name = root.file_name().unwrap().to_string_lossy().to_string();
30+
state.clients.echo_msg(&root, name, START_MSG).await;
3431

35-
echo_messsage_to_clients(&state, root, &project_name, START_MSG).await;
36-
37-
{
38-
let res = try_updating_project_state(&mut state, &path, root, &project_name).await;
39-
if res.is_err() {
40-
*debounce = std::time::SystemTime::now();
41-
res?;
42-
}
43-
}
44-
45-
ensure_server_configuration(&state, root, &project_name).await;
46-
47-
if let Err(e) = generate_compiled_commands(&state, root, &project_name).await {
32+
if let Err(e) = compile::ensure_server_support(state, name, root, Some(&path)).await {
4833
*debounce = std::time::SystemTime::now();
4934
return Err(e.into());
5035
}
5136

52-
echo_messsage_to_clients(&state, root, &project_name, SUCC_MESSAGE).await;
37+
state.clients.echo_msg(&root, name, SUCC_MESSAGE).await;
5338

5439
*debounce = std::time::SystemTime::now();
5540

5641
Ok(())
5742
}
5843

59-
async fn try_updating_project_state<'a>(
60-
state: &mut tokio::sync::MutexGuard<'a, crate::state::State>,
61-
path: &PathBuf,
62-
root: &PathBuf,
63-
project_name: &String,
64-
) -> Result<(), WatchError> {
65-
let generated = match crate::xcodegen::regenerate(path, &root).await {
66-
Ok(generated) => generated,
67-
Err(e) => {
68-
echo_error_to_clients(state, root, project_name, &e.to_string()).await;
69-
return Ok(());
70-
}
71-
};
72-
73-
if generated {
74-
state.projects.get_mut(root)?.update().await?
75-
}
76-
77-
Ok(())
78-
}
79-
80-
async fn ensure_server_configuration<'a>(
81-
state: &'a tokio::sync::MutexGuard<'a, crate::state::State>,
82-
root: &PathBuf,
83-
project_name: &String,
84-
) {
85-
if compile::ensure_server_config(&root).await.is_err() {
86-
echo_error_to_clients(
87-
state,
88-
&root,
89-
project_name,
90-
"Fail to ensure build server configuration!",
91-
)
92-
.await;
93-
}
94-
}
95-
96-
async fn generate_compiled_commands<'a>(
97-
state: &'a tokio::sync::MutexGuard<'a, crate::state::State>,
98-
root: &PathBuf,
99-
project_name: &String,
100-
) -> Result<(), Error> {
101-
if let Err(err) = compile::update_compilation_file(&root).await {
102-
echo_error_to_clients(
103-
state,
104-
&root,
105-
project_name,
106-
"Fail to regenerate compilation database!",
107-
)
108-
.await;
109-
110-
// use crate::util::proc_exists;
111-
// for (pid, client) in state.clients.iter() {
112-
// if proc_exists(pid, || {}) {
113-
// let mut logger = client.new_logger("Compile Error", project_name, &None);
114-
// logger.set_running().await.ok();
115-
// let ref win = Some(logger.open_win().await.map_err(WatchError::r#continue)?);
116-
// logger.log(err.to_string(), win).await.ok();
117-
// logger.set_status_end(false, true).await.ok();
118-
// }
119-
// }
120-
return Err(err);
121-
}
122-
123-
tracing::info!("Updated `{project_name}/.compile`");
124-
125-
Ok(())
126-
}
127-
128-
async fn echo_messsage_to_clients<'a>(
129-
state: &tokio::sync::MutexGuard<'a, crate::state::State>,
130-
root: &PathBuf,
131-
project_name: &String,
132-
msg: &str,
133-
) {
134-
state.clients.echo_msg(&root, project_name, msg).await;
135-
}
136-
137-
async fn echo_error_to_clients<'a>(
138-
state: &tokio::sync::MutexGuard<'a, crate::state::State>,
139-
root: &PathBuf,
140-
project_name: &String,
141-
msg: &str,
142-
) {
143-
state.clients.echo_err(&root, project_name, msg).await;
144-
}
145-
14644
async fn should_skip_compile(event: &Event, path: &PathBuf, last_seen: Arc<Mutex<String>>) -> bool {
147-
match &event.kind {
45+
tracing::trace!("[NewEvent] {:#?}", &event);
46+
47+
let skip = match &event.kind {
14848
EventKind::Create(_) => {
14949
tokio::time::sleep(Duration::new(1, 0)).await;
15050
tracing::debug!("[FileCreated]: {:?}", path);
51+
false
15152
}
15253

15354
EventKind::Remove(_) => {
15455
tokio::time::sleep(Duration::new(1, 0)).await;
15556
tracing::debug!("[FileRemoved]: {:?}", path);
57+
false
15658
}
15759

15860
EventKind::Modify(m) => match m {
15961
ModifyKind::Data(e) => match e {
16062
DataChange::Content => {
16163
if !path.display().to_string().contains("project.yml") {
162-
return true;
64+
true;
16365
}
16466
tokio::time::sleep(Duration::new(1, 0)).await;
16567
tracing::debug!("[XcodeGenConfigUpdate]");
16668
// HACK: Not sure why, but this is needed because xcodegen break.
69+
false
16770
}
168-
_ => return true,
71+
_ => true,
16972
},
17073

17174
ModifyKind::Name(_) => {
17275
let path_string = path.to_string_lossy();
17376
// HACK: only account for new path and skip duplications
17477
if !path.exists() || is_seen(last_seen.clone(), &path_string).await {
175-
return true;
78+
true;
17679
}
17780
tokio::time::sleep(Duration::new(1, 0)).await;
17881
tracing::debug!("[FileRenamed]: {:?}", path);
82+
false
17983
}
180-
_ => return true,
84+
_ => true,
18185
},
18286

183-
_ => return true,
87+
_ => true,
18488
};
18589

186-
false
90+
if skip {
91+
tracing::trace!("Skipping {:#?}", &event);
92+
}
93+
skip
18794
}

0 commit comments

Comments
 (0)