Skip to content

Commit

Permalink
ref(daemon): abstract some logic in Client struct
Browse files Browse the repository at this point in the history
  • Loading branch information
kkharji committed May 23, 2022
1 parent 0313b76 commit 6ee4138
Show file tree
Hide file tree
Showing 22 changed files with 242 additions and 160 deletions.
121 changes: 121 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#[cfg(feature = "daemon")]
use {
crate::{compile, nvim::NvimClient, state::State, util::fs::get_dirname_dir_root, Result},
std::path::PathBuf,
tokio::sync::MutexGuard,
};

use crate::types::Root;
use serde::{Deserialize, Serialize};

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct Client {
pub pid: i32,
pub root: Root,
pub address: String,
}

#[cfg(feature = "daemon")]
impl Client {
/// Get nvim client with state
pub fn nvim<'a>(&self, state: &'a MutexGuard<'_, State>) -> Result<&'a NvimClient> {
state.clients.get(&self.pid)
}

/// Check if client is registered in state
pub fn is_registered<'a>(&self, state: &'a MutexGuard<'_, State>) -> bool {
state.clients.contains_key(&self.pid)
}

/// Remove client from state
pub fn remove_self<'a>(&self, state: &'a mut MutexGuard<'_, State>) {
tracing::debug!("remove({})", self.pid);
state.clients.remove(&self.pid);
}

/// Remove client from state
pub async fn register_self<'a>(&self, state: &'a mut MutexGuard<'_, State>) -> Result<()> {
state.clients.add(self).await
}

/// Register project if it's not already registered
pub async fn register_project<'a>(&self, state: &'a mut MutexGuard<'_, State>) -> Result<()> {
if let Ok(project) = state.projects.get_mut(&self.root) {
project.clients.push(self.pid);
} else {
state.projects.add(self).await?;
let ignore_pattern = state
.projects
.get(&self.root)
.unwrap()
.ignore_patterns
.clone();

state
.watcher
.add_project_watcher(self, ignore_pattern)
.await
}

Ok(())
}

/// Remove project root watcher
pub async fn remove_watcher<'a>(&self, state: &'a mut MutexGuard<'_, State>) {
state.watcher.remove_project_watcher(self).await;
}

pub async fn ensure_server_support<'a>(
&self,
state: &'a mut MutexGuard<'_, State>,
path: Option<&PathBuf>,
) -> Result<bool> {
compile::ensure_server_support(state, self, path).await
}
}

#[cfg(feature = "daemon")]
impl Client {
pub fn abbrev_root(&self) -> String {
get_dirname_dir_root(&self.root).unwrap_or_default()
}
}

#[cfg(feature = "lua")]
use {crate::util::mlua::LuaExtension, mlua::prelude::*, tap::Pipe};

#[cfg(feature = "lua")]
impl Client {
/// Derive client from lua value
/// lua value can:
/// - Client key with table value within it a key with "root"
/// - Client key with string value representing "root"
/// If value is none, then current working directory will be used
/// lua value can either be a table with client key being a string
pub fn derive(lua: &Lua, value: Option<LuaValue>) -> LuaResult<Self> {
let root = match value {
Some(LuaValue::Table(ref table)) => table.get("root")?,
Some(LuaValue::String(ref root)) => root.to_string_lossy().to_string(),
_ => lua.cwd()?,
};
Self {
pid: std::process::id() as i32,
address: lua.nvim_address()?,
root: root.into(),
}
.pipe(Ok)
}
}

#[cfg(feature = "lua")]
impl<'a> mlua::FromLua<'a> for Client {
fn from_lua(value: mlua::Value<'a>, lua: &'a mlua::Lua) -> mlua::Result<Self> {
Self::derive(
lua,
match value {
LuaValue::Nil => None,
_ => Some(value),
},
)
}
}
29 changes: 17 additions & 12 deletions src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub async fn generate_from_steps(steps: &Vec<Step>) -> Result<CompilationDatabas
#[cfg(feature = "daemon")]
use {
crate::{
client::Client,
error::{CompileError, XcodeGenError},
state::State,
util::pid,
Expand All @@ -138,7 +139,7 @@ use {
tokio::{
fs::{metadata, File},
io::AsyncWriteExt,
sync::OwnedMutexGuard,
sync::MutexGuard,
},
};

Expand Down Expand Up @@ -193,24 +194,27 @@ pub async fn ensure_server_config(root: &path::PathBuf) -> Result<()> {

#[cfg(feature = "daemon")]
pub async fn ensure_server_support<'a>(
state: &'a mut OwnedMutexGuard<State>,
name: &String,
root: &PathBuf,
state: &'a mut MutexGuard<'_, State>,
client: &Client,
path: Option<&PathBuf>,
) -> Result<bool> {
let compile_exists = metadata(root.join(".compile")).await.is_ok();
let Client { root, .. } = client;
let ref name = client.abbrev_root();

let compile_path = root.join(".compile");
let compile_exists = metadata(compile_path).await.is_ok();

if ensure_server_config(&root).await.is_err() {
"fail to ensure build server configuration!"
.pipe(|msg| state.clients.echo_err(&root, &name, msg))
.pipe(|msg| state.clients.echo_err(root, name, msg))
.await;
}

if let Some(path) = path {
let generated = match xcodegen::regenerate(path, &root).await {
Ok(generated) => generated,
Err(e) => {
state.clients.echo_err(&root, &name, &e.to_string()).await;
state.clients.echo_err(&root, name, &e.to_string()).await;
return Err(e);
}
};
Expand All @@ -224,14 +228,14 @@ pub async fn ensure_server_support<'a>(

if xcodegen::is_valid(&root) && path.is_none() {
"⚙ generating xcodeproj ..."
.pipe(|msg| state.clients.echo_msg(&root, &name, msg))
.pipe(|msg| state.clients.echo_msg(root, name, msg))
.await;

if let Some(err) = match xcodegen::generate(&root).await {
Ok(status) => {
if status.success() {
"setup: ⚙ generate xcodeproj ..."
.pipe(|msg| state.clients.echo_msg(&root, &name, msg))
.pipe(|msg| state.clients.echo_msg(root, name, msg))
.await;
None
} else {
Expand All @@ -240,19 +244,20 @@ pub async fn ensure_server_support<'a>(
}
Err(e) => Some(e),
} {
let msg = format!("fail to generate xcodeproj: {err}");
state.clients.echo_err(&root, &name, &msg).await;
let ref msg = format!("fail to generate xcodeproj: {err}");
state.clients.echo_err(root, name, msg).await;
}
};

// TODO(compile): check for .xcodeproj if project.yml is not generated
if !compile_exists {
"⚙ generating compile database .."
.pipe(|msg| state.clients.echo_msg(&root, &name, msg))
.pipe(|msg| state.clients.echo_msg(root, name, msg))
.await;
}

// The following command won't successed if this file doesn't exists

if let Err(err) = update_compilation_file(&root).await {
"setup: fail to regenerate compilation database!"
.pipe(|msg| state.clients.echo_err(&root, &name, msg))
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::Result;
#[cfg(feature = "daemon")]
use async_trait::async_trait;

use crate::types::Client;
use crate::client::Client;

use serde::{Deserialize, Serialize};

Expand Down
12 changes: 7 additions & 5 deletions src/daemon/requests/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ pub struct Build {
impl Handler for Build {
async fn handle(self) -> Result<()> {
let Self { client, config, .. } = &self;
let Client { pid, root, .. } = client;
let Client { root, .. } = client;

tracing::debug!("Handling build request {:#?}", config);

let state = DAEMON_STATE.clone().lock_owned().await;
let nvim = state.clients.get(pid)?;
let state = DAEMON_STATE.clone();
let ref state = state.lock().await;

let nvim = client.nvim(state)?;
let direction = self.direction.clone();

let args = append_build_root(&root, config.as_args())?;
Expand All @@ -41,8 +43,8 @@ impl Handler for Build {
let success = build_with_loggger(logger, &root, &args, true, true).await?;

if !success {
nvim.echo_err(&format!("Failed: {} ", config.to_string()))
.await?;
let ref msg = format!("Failed: {} ", config.to_string());
nvim.echo_err(msg).await?;
};

Ok(())
Expand Down
14 changes: 5 additions & 9 deletions src/daemon/requests/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,20 @@ impl Handler for Drop {
} = self;

let state = DAEMON_STATE.clone();
let mut state = state.lock().await;
let ref mut state = state.lock().await;

if state.clients.contains_key(&client.pid) {
if client.is_registered(state) {
tracing::info!("Drop({}: {})", client.pid, client.abbrev_root());
// NOTE: Should only be Some if no more client depend on it
if let Some(project) = state.projects.remove(&client).await? {
// NOTE: Remove project watchers
state.watcher.remove_project_watcher(&client).await;
client.remove_watcher(state).await;
// NOTE: Remove target watchers associsated with root
state
.watcher
.remove_target_watcher_for_root(&project.root)
.await;
project.remove_target_watchers(state).await;
}
// NOTE: Try removing client with given pid
if remove_client {
tracing::debug!("RemoveClient({})", client.pid);
state.clients.remove(&client.pid);
client.remove_self(state);
}
// NOTE: Sink state to all client vim.g.xbase.state
state.sync_client_state().await?;
Expand Down
47 changes: 11 additions & 36 deletions src/daemon/requests/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,26 @@ use super::*;
/// Register new client with workspace
#[derive(Debug, Serialize, Deserialize)]
pub struct Register {
pub client: crate::types::Client,
pub client: Client,
}

#[cfg(feature = "daemon")]
use {
crate::{compile::ensure_server_support, constants::DAEMON_STATE},
tap::Pipe,
};
use crate::constants::DAEMON_STATE;

#[cfg(feature = "daemon")]
#[async_trait]
impl Handler for Register {
async fn handle(self) -> Result<()> {
let Self { client } = &self;
let Client { pid, root, .. } = &client;
let name = client.abbrev_root();
let ref mut state = DAEMON_STATE.clone().lock_owned().await;

tracing::info!("Register({pid}, {name}): ");

// NOTE: Create nvim client
state.clients.add(client).await?;

if let Ok(project) = state.projects.get_mut(root) {
// NOTE: Add client pid to project
project.clients.push(*pid);
} else {
// NOTE: Create nvim client
state.projects.add(client).await?;

let project = state.projects.get(root).unwrap();
let ignore_patterns = project.ignore_patterns.clone();

// NOTE: Add watcher
state
.watcher
.add_project_watcher(client, ignore_patterns)
.await
}

// NOTE: Ensure project is ready for xbase build server
if ensure_server_support(state, &name, &self.client.root, None).await? {
"setup: ✅"
.pipe(|msg| state.clients.echo_msg(&self.client.root, &name, msg))
.await;
let Client { root, .. } = &client;
let state = DAEMON_STATE.clone();
let ref mut state = state.lock().await;

client.register_self(state).await?;
client.register_project(state).await?;
if client.ensure_server_support(state, None).await? {
let ref name = client.abbrev_root();
state.clients.echo_msg(root, name, "setup: ✅").await;
}

// NOTE: Sink Daemon to nvim vim.g.xbase
Expand Down
12 changes: 5 additions & 7 deletions src/daemon/requests/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,15 @@ pub struct Run {
#[async_trait::async_trait]
impl Handler for Run {
async fn handle(self) -> Result<()> {
let Client { pid, root, .. } = &self.client;
tracing::info!("{:#?}", self);
let Client { root, .. } = &self.client;

tracing::info!("⚙️ Running command: {}", self.config.to_string());

let state = DAEMON_STATE.clone().lock_owned().await;
let state = DAEMON_STATE.clone();
let ref state = state.lock().await;
let device = state.devices.from_lookup(self.device);
tracing::info!("{:#?}", device);

let nvim = state.clients.get(&pid)?;
let nvim = self.client.nvim(state)?;
let args = {
let mut args = self.config.as_args();
if let Some(ref device) = device {
Expand Down Expand Up @@ -74,12 +73,11 @@ impl Handler for Run {
target: self.config.target,
platform,
client: self.client,
state,
args,
udid: device.map(|d| d.udid.clone()),
direction: self.direction,
}
.run(settings)
.run(state, settings)
.await?;

Ok(())
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pub mod types;

pub mod util;

#[cfg(any(feature = "daemon", feature = "lua"))]
mod client;
#[cfg(any(feature = "daemon", feature = "lua"))]
mod nvim;

#[cfg(feature = "daemon")]
Expand Down
2 changes: 1 addition & 1 deletion src/nvim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};

#[cfg(feature = "daemon")]
use {
crate::{types::Client, Result},
crate::{client::Client, Result},
nvim_rs::{compat::tokio::Compat, create::tokio::new_path as connect, rpc::handler::Dummy},
};

Expand Down
Loading

0 comments on commit 6ee4138

Please sign in to comment.