From 6ee4138bf33c0d4e3122d36ef45b4ead48996fb4 Mon Sep 17 00:00:00 2001 From: tami5 Date: Mon, 23 May 2022 13:23:45 +0300 Subject: [PATCH] ref(daemon): abstract some logic in Client struct --- src/client.rs | 121 ++++++++++++++++++++++++++++++++ src/compile.rs | 29 ++++---- src/daemon/requests.rs | 2 +- src/daemon/requests/build.rs | 12 ++-- src/daemon/requests/drop.rs | 14 ++-- src/daemon/requests/register.rs | 47 +++---------- src/daemon/requests/run.rs | 12 ++-- src/lib.rs | 3 + src/nvim.rs | 2 +- src/runner.rs | 33 ++++++--- src/state.rs | 2 +- src/store/clients.rs | 2 +- src/store/mod.rs | 6 +- src/store/projects.rs | 2 +- src/store/runners.rs | 10 +++ src/store/watcher.rs | 3 +- src/types.rs | 2 - src/types/client.rs | 58 --------------- src/types/project.rs | 12 +++- src/watcher/handle.rs | 2 +- src/watcher/handle/project.rs | 25 ++++--- src/watcher/handle/target.rs | 3 +- 22 files changed, 242 insertions(+), 160 deletions(-) create mode 100644 src/client.rs create mode 100644 src/store/runners.rs delete mode 100644 src/types/client.rs diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..36ba055 --- /dev/null +++ b/src/client.rs @@ -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 { + 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) -> LuaResult { + 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::derive( + lua, + match value { + LuaValue::Nil => None, + _ => Some(value), + }, + ) + } +} diff --git a/src/compile.rs b/src/compile.rs index 43143e0..6555d81 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -127,6 +127,7 @@ pub async fn generate_from_steps(steps: &Vec) -> Result Result<()> { #[cfg(feature = "daemon")] pub async fn ensure_server_support<'a>( - state: &'a mut OwnedMutexGuard, - name: &String, - root: &PathBuf, + state: &'a mut MutexGuard<'_, State>, + client: &Client, path: Option<&PathBuf>, ) -> Result { - 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; } @@ -210,7 +214,7 @@ pub async fn ensure_server_support<'a>( 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); } }; @@ -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 { @@ -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)) diff --git a/src/daemon/requests.rs b/src/daemon/requests.rs index 38cadae..248162b 100644 --- a/src/daemon/requests.rs +++ b/src/daemon/requests.rs @@ -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}; diff --git a/src/daemon/requests/build.rs b/src/daemon/requests/build.rs index dc8f391..7477b44 100644 --- a/src/daemon/requests/build.rs +++ b/src/daemon/requests/build.rs @@ -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())?; @@ -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(()) diff --git a/src/daemon/requests/drop.rs b/src/daemon/requests/drop.rs index 4f259e8..5d6e338 100644 --- a/src/daemon/requests/drop.rs +++ b/src/daemon/requests/drop.rs @@ -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?; diff --git a/src/daemon/requests/register.rs b/src/daemon/requests/register.rs index 7a9706c..cf779b8 100644 --- a/src/daemon/requests/register.rs +++ b/src/daemon/requests/register.rs @@ -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 diff --git a/src/daemon/requests/run.rs b/src/daemon/requests/run.rs index 53e7da1..dbfb5f0 100644 --- a/src/daemon/requests/run.rs +++ b/src/daemon/requests/run.rs @@ -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 { @@ -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(()) diff --git a/src/lib.rs b/src/lib.rs index 51d997b..406c41b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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")] diff --git a/src/nvim.rs b/src/nvim.rs index c3fe3cb..b925ad1 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -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}, }; diff --git a/src/runner.rs b/src/runner.rs index 3e72939..450a611 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,19 +1,22 @@ mod simctl; +use tokio::sync::MutexGuard; + pub use self::simctl::*; use crate::{ + client::Client, constants::DAEMON_STATE, nvim::BufferDirection, state::State, - types::{Client, Platform}, + types::Platform, util::{fmt, pid}, Error, Result, }; use { process_stream::{Process, ProcessItem, StreamExt}, tap::Pipe, - tokio::{sync::OwnedMutexGuard, task::JoinHandle}, + tokio::task::JoinHandle, xcodebuild::parser::BuildSettings, }; @@ -21,26 +24,33 @@ pub struct Runner { pub client: Client, pub target: String, pub platform: Platform, - pub state: OwnedMutexGuard, pub udid: Option, pub direction: BufferDirection, pub args: Vec, } impl Runner { - pub async fn run(self, settings: BuildSettings) -> Result>> { + pub async fn run<'a>( + self, + state: &'a MutexGuard<'_, State>, + settings: BuildSettings, + ) -> Result>> { if self.platform.is_mac_os() { - return self.run_as_macos_app(settings).await; + return self.run_as_macos_app(state, settings).await; } else { - return self.run_with_simctl(settings).await; + return self.run_with_simctl(state, settings).await; } } } /// MacOS Runner impl Runner { - pub async fn run_as_macos_app(self, settings: BuildSettings) -> Result>> { - let nvim = self.state.clients.get(&self.client.pid)?; + pub async fn run_as_macos_app<'a>( + self, + state: &'a MutexGuard<'_, State>, + settings: BuildSettings, + ) -> Result>> { + let nvim = self.client.nvim(state)?; let ref mut logger = nvim.logger(); logger.log_title().await?; @@ -84,11 +94,14 @@ impl Runner { /// Simctl Runner impl Runner { - pub async fn run_with_simctl(self, settings: BuildSettings) -> Result>> { + pub async fn run_with_simctl<'a>( + self, + state: &'a MutexGuard<'_, State>, + settings: BuildSettings, + ) -> Result>> { let Self { client, target, - state, udid, .. } = self; diff --git a/src/state.rs b/src/state.rs index a79dfa2..a7b72dc 100644 --- a/src/state.rs +++ b/src/state.rs @@ -95,7 +95,7 @@ impl State { #[allow(dead_code)] async fn get_client_projects<'a>( &'a self, - client: &'a crate::types::Client, + client: &'a crate::client::Client, ) -> Result> { self.projects .iter() diff --git a/src/store/clients.rs b/src/store/clients.rs index ab7e83a..5203005 100644 --- a/src/store/clients.rs +++ b/src/store/clients.rs @@ -1,5 +1,5 @@ +use crate::client::Client; use crate::nvim::NvimClient; -use crate::types::Client; use crate::{LoopError, Result}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/store/mod.rs b/src/store/mod.rs index 5b37101..02c7979 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -3,10 +3,8 @@ mod runners; pub use projects::ProjectStore; -#[cfg(feature = "daemon")] -mod simdevices; -#[cfg(feature = "daemon")] -pub use simdevices::*; +mod devices; +pub use devices::*; #[cfg(feature = "daemon")] mod clients; diff --git a/src/store/projects.rs b/src/store/projects.rs index 2574feb..439fa04 100644 --- a/src/store/projects.rs +++ b/src/store/projects.rs @@ -1,7 +1,7 @@ #[cfg(feature = "daemon")] use crate::{ + client::Client, error::{EnsureOptional, LoopError}, - types::Client, Result, }; diff --git a/src/store/runners.rs b/src/store/runners.rs new file mode 100644 index 0000000..5125eae --- /dev/null +++ b/src/store/runners.rs @@ -0,0 +1,10 @@ +// use crate::daemon::WatchTarget; +// use crate::watcher::WatchHandler; + +// use crate::types::{Client, Root}; +// use serde::{Deserialize, Serialize}; +// use std::collections::HashMap; +// use std::path::PathBuf; + +// #[derive(Default, Debug, Deserialize, Serialize)] +// pub struct RunnersStore(HashMap<>) diff --git a/src/store/watcher.rs b/src/store/watcher.rs index df06ee9..76c97dd 100644 --- a/src/store/watcher.rs +++ b/src/store/watcher.rs @@ -1,7 +1,8 @@ use crate::daemon::WatchTarget; use crate::watcher::WatchHandler; -use crate::types::{Client, Root}; +use crate::client::Client; +use crate::types::Root; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::PathBuf; diff --git a/src/types.rs b/src/types.rs index 4afaf52..5c06539 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,7 @@ mod build; -mod client; mod project; pub use build::*; -pub use client::*; pub use project::*; #[cfg(feature = "daemon")] diff --git a/src/types/client.rs b/src/types/client.rs deleted file mode 100644 index f0b9b71..0000000 --- a/src/types/client.rs +++ /dev/null @@ -1,58 +0,0 @@ -#[cfg(feature = "daemon")] -use crate::util::fs::get_dirname_dir_root; - -use super::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 { - 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) -> LuaResult { - 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::derive( - lua, - match value { - LuaValue::Nil => None, - _ => Some(value), - }, - ) - } -} diff --git a/src/types/project.rs b/src/types/project.rs index 62d3a9a..07255f7 100644 --- a/src/types/project.rs +++ b/src/types/project.rs @@ -19,7 +19,10 @@ pub use { }; #[cfg(feature = "daemon")] -use crate::{error::EnsureOptional, xcodegen, Result}; +use { + crate::{error::EnsureOptional, state::State, xcodegen, Result}, + tokio::sync::MutexGuard, +}; /// Represent XcodeGen Project #[derive(Debug, Deserialize, Serialize)] @@ -106,4 +109,11 @@ impl Project { }, } } + + pub async fn remove_target_watchers<'a>(&self, state: &'a mut MutexGuard<'_, State>) { + state + .watcher + .remove_target_watcher_for_root(&self.root) + .await; + } } diff --git a/src/watcher/handle.rs b/src/watcher/handle.rs index f17282c..65f00bb 100644 --- a/src/watcher/handle.rs +++ b/src/watcher/handle.rs @@ -1,5 +1,5 @@ use crate::error::WatchError; -use crate::{daemon::WatchTarget, types::Client}; +use crate::{client::Client, daemon::WatchTarget}; use anyhow::Result; use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/src/watcher/handle/project.rs b/src/watcher/handle/project.rs index 7e6ceb0..7915b0a 100644 --- a/src/watcher/handle/project.rs +++ b/src/watcher/handle/project.rs @@ -1,6 +1,5 @@ use super::{is_seen, WatchArguments, WatchError}; -use crate::compile; -use crate::{constants::DAEMON_STATE, types::Client}; +use crate::constants::DAEMON_STATE; use notify::event::{DataChange, ModifyKind}; use notify::{Event, EventKind}; use std::{path::PathBuf, sync::Arc, time::Duration}; @@ -17,7 +16,7 @@ pub async fn create(args: WatchArguments) -> Result<(), WatchError> { } = args; let info = info.lock_owned().await; - let Client { root, .. } = info.try_into_project()?; + let client = info.try_into_project()?; if should_skip_compile(&event, &path, args.last_seen).await { tracing::debug!("Skipping {:?}", &event.paths); @@ -26,18 +25,28 @@ pub async fn create(args: WatchArguments) -> Result<(), WatchError> { tracing::trace!("[NewEvent] {:#?}", &event); - let ref name = root.file_name().unwrap().to_string_lossy().to_string(); - let ref mut state = DAEMON_STATE.clone().lock_owned().await; + let ref name = client + .root + .file_name() + .unwrap() + .to_string_lossy() + .to_string(); + + let state = DAEMON_STATE.clone(); + let ref mut state = state.lock().await; let mut debounce = args.debounce.lock().await; - state.clients.echo_msg(&root, name, START_MSG).await; + state.clients.echo_msg(&client.root, name, START_MSG).await; - if let Err(e) = compile::ensure_server_support(state, name, root, Some(&path)).await { + if let Err(e) = client.ensure_server_support(state, Some(&path)).await { *debounce = std::time::SystemTime::now(); return Err(e.into()); } - state.clients.echo_msg(&root, name, SUCC_MESSAGE).await; + state + .clients + .echo_msg(&client.root, name, SUCC_MESSAGE) + .await; *debounce = std::time::SystemTime::now(); diff --git a/src/watcher/handle/target.rs b/src/watcher/handle/target.rs index 9a06a57..60b44d9 100644 --- a/src/watcher/handle/target.rs +++ b/src/watcher/handle/target.rs @@ -1,7 +1,8 @@ use super::{WatchArguments, WatchError}; +use crate::client::Client; use crate::constants::DAEMON_STATE; use crate::daemon::{WatchKind, WatchTarget}; -use crate::types::{BuildConfiguration, Client}; +use crate::types::BuildConfiguration; use crate::xcode::{append_build_root, build_with_loggger}; use anyhow::Result; use notify::{event::ModifyKind, Event, EventKind};