Skip to content

Commit

Permalink
ref(daemon): organization
Browse files Browse the repository at this point in the history
  • Loading branch information
kkharji committed Apr 27, 2022
1 parent 7da58b6 commit be4b5ed
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 179 deletions.
2 changes: 1 addition & 1 deletion lua/xcodebase/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use mlua::lua_module;
use mlua::prelude::*;
use xcodebase::Daemon;
use xcodebase::daemon::Daemon;

#[lua_module]
fn libxcodebase(lua: &Lua) -> LuaResult<LuaTable> {
Expand Down
104 changes: 67 additions & 37 deletions src/daemon.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Handle requests from neovim and manage dev workflow
use anyhow::{bail, Context, Result};
mod requests;
pub mod state;

Expand All @@ -8,55 +7,71 @@ pub use requests::*;
#[cfg(feature = "daemon")]
pub use state::DaemonState;

#[cfg(feature = "lua")]
use crate::util::mlua::LuaExtension;

#[cfg(feature = "lua")]
use mlua::prelude::*;

#[cfg(feature = "daemon")]
use anyhow::Result;

pub const DAEMON_SOCKET_PATH: &str = "/tmp/xcodebase-daemon.socket";
pub const DAEMON_BINARY: &str =
"/Users/tami5/repos/neovim/xcodebase.nvim/target/debug/xcodebase-daemon";

/// Representation of daemon
pub struct Daemon {
#[cfg(feature = "daemon")]
pub state: std::sync::Arc<tokio::sync::Mutex<state::DaemonStateData>>,
#[cfg(feature = "daemon")]
pub listener: tokio::net::UnixListener,
pub struct Daemon;

/// Requirement for daemon actions
#[cfg(feature = "daemon")]
#[async_trait::async_trait]
pub trait DaemonRequestHandler<T> {
async fn handle(&self, state: DaemonState) -> Result<()>;
fn parse(args: Vec<&str>) -> Result<T>;
}

impl Daemon {
#[cfg(feature = "daemon")]
pub fn new() -> Self {
Self {
state: Default::default(),
listener: tokio::net::UnixListener::bind(DAEMON_SOCKET_PATH).unwrap(),
}
}
/// Representations of all the supported daemon requests
#[derive(Debug)]
pub enum DaemonRequest {
Build(Build),
Run(Run),
RenameFile(RenameFile),
Register(Register),
Drop(Drop),
}

/// Spawn new instance of the server via running binaray is a child process
pub fn spawn() -> Result<()> {
std::process::Command::new(DAEMON_BINARY)
.spawn()
.context("Unable to start background instance using daemon binaray")
.map(|_| ())
#[cfg(feature = "daemon")]
impl DaemonRequest {
/// Handle daemon request
pub async fn handle(&self, state: DaemonState) -> Result<()> {
match self {
DaemonRequest::Build(c) => c.handle(state).await,
DaemonRequest::Run(c) => c.handle(state).await,
DaemonRequest::RenameFile(c) => c.handle(state).await,
DaemonRequest::Register(c) => c.handle(state).await,
DaemonRequest::Drop(c) => c.handle(state).await,
}
}

/// Pass args to running daemon
pub fn execute(args: &[&str]) -> Result<()> {
use std::io::Write;
match std::os::unix::net::UnixStream::connect(DAEMON_SOCKET_PATH) {
Ok(mut stream) => {
stream.write_all(args.join(" ").as_str().as_ref())?;
stream.flush().context("Fail to flush stream!")
}
Err(e) => bail!("Fail to execute {:#?}: {e}", args),
}
/// Parse [`super::Daemon`] request from string
pub fn parse(str: &str) -> Result<Self> {
let mut args = str.split(" ").collect::<Vec<&str>>();
Ok(match args.remove(0) {
Build::KEY => Self::Build(Build::parse(args)?),
Run::KEY => Self::Run(Run::parse(args)?),
RenameFile::KEY => Self::RenameFile(RenameFile::parse(args)?),
Register::KEY => Self::Register(Register::parse(args)?),
Drop::KEY => Self::Drop(Drop::parse(args)?),
cmd => anyhow::bail!("Unknown command messsage: {cmd}"),
})
}
}

#[cfg(feature = "lua")]
use crate::util::mlua::LuaExtension;

#[cfg(feature = "lua")]
impl Daemon {
/// Representation of daemon table in lua
pub fn lua(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
pub fn lua(lua: &mlua::Lua) -> LuaResult<LuaTable> {
let table = lua.create_table()?;
table.set("is_running", lua.create_function(Self::is_running)?)?;
table.set("ensure", lua.create_function(Self::ensure)?)?;
Expand All @@ -66,22 +81,37 @@ impl Daemon {
}

/// Check if Daemon is running
pub fn is_running(_: &mlua::Lua, _: ()) -> mlua::Result<bool> {
pub fn is_running(_: &mlua::Lua, _: ()) -> LuaResult<bool> {
match std::os::unix::net::UnixStream::connect(DAEMON_SOCKET_PATH) {
Ok(stream) => Ok(stream.shutdown(std::net::Shutdown::Both).ok().is_some()),
Err(_) => Ok(false),
}
}

/// Ensure that daemon is currently running in background
pub fn ensure(lua: &mlua::Lua, _: ()) -> mlua::Result<bool> {
pub fn ensure(lua: &mlua::Lua, _: ()) -> LuaResult<bool> {
if Self::is_running(lua, ()).unwrap() {
Ok(false)
} else if Self::spawn().is_ok() {
} else if std::process::Command::new(DAEMON_BINARY).spawn().is_ok() {
lua.info("Spawned Background Server")?;
Ok(true)
} else {
panic!("Unable to spawn background server");
}
}

/// Pass args to running daemon
pub fn execute(args: &[&str]) -> LuaResult<()> {
use std::io::Write;
match std::os::unix::net::UnixStream::connect(DAEMON_SOCKET_PATH) {
Ok(mut stream) => {
stream.write_all(args.join(" ").as_str().as_ref())?;
stream.flush().map_err(mlua::Error::external)
}
Err(e) => Err(mlua::Error::external(format!(
"Fail to execute {:#?}: {e}",
args
))),
}
}
}
10 changes: 7 additions & 3 deletions src/daemon/bin.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::sync::Arc;

use tokio::io::AsyncReadExt;
use tokio::sync::Mutex;
use xcodebase::util::tracing::install_tracing;
use xcodebase::{daemon::*, util::watch};

Expand All @@ -12,11 +15,12 @@ async fn main() -> anyhow::Result<()> {

tracing::info!("Started");

let daemon = Daemon::new();
let state: Arc<Mutex<state::DaemonStateData>> = Default::default();
let listener = tokio::net::UnixListener::bind(DAEMON_SOCKET_PATH).unwrap();

loop {
let state = daemon.state.clone();
let (mut s, _) = daemon.listener.accept().await.unwrap();
let state = state.clone();
let (mut s, _) = listener.accept().await.unwrap();
tokio::spawn(async move {
let mut string = String::default();

Expand Down
48 changes: 0 additions & 48 deletions src/daemon/requests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use anyhow::Result;

mod build;
mod drop;
mod register;
Expand All @@ -11,49 +9,3 @@ pub use drop::Drop;
pub use register::Register;
pub use rename_file::RenameFile;
pub use run::Run;

/// Requirement for daemon actions
#[async_trait::async_trait]
#[cfg(feature = "daemon")]
pub trait DaemonRequestHandler {
async fn handle(&self, state: super::DaemonState) -> Result<()>;
}

/// Representations of all the supported daemon requests
#[derive(Debug)]
pub enum DaemonRequest {
Build(Build),
Run(Run),
RenameFile(RenameFile),
Register(Register),
Drop(Drop),
}

impl DaemonRequest {
#[cfg(feature = "daemon")]
/// Handle daemon request
pub async fn handle(&self, state: super::DaemonState) -> Result<()> {
use DaemonRequest::*;

match self {
Build(c) => c.handle(state).await,
Run(c) => c.handle(state).await,
RenameFile(c) => c.handle(state).await,
Register(c) => c.handle(state).await,
Drop(c) => c.handle(state).await,
}
}

/// Parse [`super::Daemon`] request from string
pub fn parse(str: &str) -> Result<Self> {
let mut args = str.split(" ").collect::<Vec<&str>>();
Ok(match args.remove(0) {
Build::KEY => Self::Build(args.try_into()?),
Run::KEY => Self::Run(args.try_into()?),
RenameFile::KEY => Self::RenameFile(args.try_into()?),
Register::KEY => Self::Register(args.try_into()?),
Drop::KEY => Self::Drop(args.try_into()?),
cmd => anyhow::bail!("Unknown command messsage: {cmd}"),
})
}
}
38 changes: 20 additions & 18 deletions src/daemon/requests/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
#[cfg(feature = "mlua")]
use crate::daemon::Daemon;

#[cfg(feature = "daemon")]
use crate::daemon::{DaemonRequestHandler, DaemonState};

#[cfg(feature = "daemon")]
use anyhow::Result;

/// Build a project.
Expand All @@ -9,45 +15,41 @@ pub struct Build {
pub scheme: Option<String>,
}

impl Build {
pub const KEY: &'static str = "build";
}

// TODO: Implement build command
// On neovim side:
// - Call the command after picking the target. If their is only a single target then just use that
// - This requires somehow given the client all information it needs in order present the user
// with the options needed to build
#[cfg(feature = "daemon")]
#[async_trait::async_trait]
impl crate::daemon::DaemonRequestHandler for Build {
async fn handle(&self, _state: crate::daemon::DaemonState) -> Result<()> {
tracing::info!("build command");
Ok(())
}
}

impl TryFrom<Vec<&str>> for Build {
type Error = anyhow::Error;

fn try_from(_args: Vec<&str>) -> Result<Self, Self::Error> {
impl DaemonRequestHandler<Build> for Build {
fn parse(_args: Vec<&str>) -> Result<Self> {
Ok(Self {
target: None,
configuration: None,
scheme: None,
})
}
}

impl Build {
pub const KEY: &'static str = "build";

pub fn request(target: &str, configuration: &str, scheme: &str) -> Result<()> {
Daemon::execute(&[KEY, target, configuration, scheme])
async fn handle(&self, _state: DaemonState) -> Result<()> {
tracing::info!("build command");
Ok(())
}
}

#[cfg(feature = "lua")]
impl Build {
#[cfg(feature = "lua")]
pub fn lua(lua: &mlua::Lua, (t, c, s): (String, String, String)) -> mlua::Result<()> {
use crate::util::mlua::LuaExtension;
lua.trace(format!("Build (target: {t} configuration: {c}, scheme: {s})").as_ref())?;
Self::request(&t, &c, &s).map_err(mlua::Error::external)
}

pub fn request(target: &str, configuration: &str, scheme: &str) -> mlua::Result<()> {
Daemon::execute(&["build", target, configuration, scheme])
}
}
47 changes: 26 additions & 21 deletions src/daemon/requests/drop.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#[cfg(feature = "mlua")]
use crate::daemon::Daemon;

#[cfg(feature = "daemon")]
use crate::daemon::{DaemonRequestHandler, DaemonState};

#[cfg(feature = "daemon")]
use anyhow::Result;

/// Drop a client
Expand All @@ -7,23 +14,14 @@ pub struct Drop {
pub root: String,
}

#[cfg(feature = "daemon")]
#[async_trait::async_trait]
impl crate::daemon::DaemonRequestHandler for Drop {
async fn handle(&self, state: crate::daemon::DaemonState) -> Result<()> {
tracing::trace!("{:?}", self);
state
.lock()
.await
.remove_workspace(&self.root, self.pid)
.await
}
impl Drop {
pub const KEY: &'static str = "drop";
}

impl TryFrom<Vec<&str>> for Drop {
type Error = anyhow::Error;

fn try_from(args: Vec<&str>) -> Result<Self, Self::Error> {
#[cfg(feature = "daemon")]
#[async_trait::async_trait]
impl DaemonRequestHandler<Drop> for Drop {
fn parse(args: Vec<&str>) -> Result<Self> {
if let (Some(pid), Some(root)) = (args.get(0), args.get(1)) {
Ok(Self {
pid: pid.parse::<i32>()?,
Expand All @@ -33,16 +31,23 @@ impl TryFrom<Vec<&str>> for Drop {
anyhow::bail!("Missing arugments: {:?}", args)
}
}
async fn handle(&self, state: DaemonState) -> Result<()> {
tracing::trace!("{:?}", self);
state
.lock()
.await
.remove_workspace(&self.root, self.pid)
.await
}
}

#[cfg(feature = "lua")]
impl Drop {
pub const KEY: &'static str = "drop";
pub fn request(pid: i32, root: String) -> Result<()> {
crate::daemon::Daemon::execute(&[Self::KEY, pid.to_string().as_str(), root.as_str()])
}

#[cfg(feature = "lua")]
pub fn lua(_: &mlua::Lua, (pid, root): (i32, String)) -> mlua::Result<()> {
Self::request(pid, root).map_err(mlua::Error::external)
}

pub fn request(pid: i32, root: String) -> mlua::Result<()> {
Daemon::execute(&[Self::KEY, pid.to_string().as_str(), root.as_str()])
}
}
Loading

0 comments on commit be4b5ed

Please sign in to comment.