Skip to content

Commit

Permalink
ref(daemon): resuse serde and lua for serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
kkharji committed May 8, 2022
1 parent a6d7251 commit e1ac069
Show file tree
Hide file tree
Showing 18 changed files with 400 additions and 446 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ default = [ ]
xcodegen = [ "async", "dirs" ]
daemon = [ "serial", "compilation", "logging", "async", "watcher", "proc", "xcodegen", "parity-tokio-ipc", "nvim-rs", "async-stream", "tokio-stream", "strum" ]
server = [ "serial", "logging", "dirs", "bsp-server", "url", "wax", "shell-words", "compilation" ]
lua = [ "mlua" ]
lua = [ "mlua", "serial" ]
serial = [ "serde", "serde_json", "serde_yaml" ]
compilation = [ "serial", "lazy_static", "shell-words", "xcodebuild" ]
logging = [ "tracing", "tracing-appender", "tracing-subscriber" ]
Expand Down
72 changes: 31 additions & 41 deletions lua/xcodebase/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,6 @@ local M = {}
local config = require "xcodebase.config"
local lib = require "libxcodebase"
local pid = vim.fn.getpid()
local address = vim.env.NVIM_LISTEN_ADDRESS

---@class XcodeBaseDaemon
---@field ensure fun():boolean: When the a new server started it should return true
---@field is_running fun():boolean
---@field register fun(pid: number, root: string):boolean
M.daemon = {}

M.project_info = function(root)
require("xcodebase.state").projects[root] = nil
lib.daemon.project_info(pid, root)
while require("xcodebase.state").projects[root] == nil do
end
end

M.drop = function()
local root = vim.loop.cwd()
lib.daemon.drop(pid, root)
end

M.build = function(target, configuration, scheme)
local root = vim.loop.cwd()
lib.daemon.build(pid, root, target, configuration, scheme)
end

---@class XcodeBaseCommand
local command = lib.command

---@class XcodeBaseService
local command = lib.service

---Check whether the vim instance should be registered to xcodebase server.
---NOTE: Only support project.yml
Expand All @@ -42,23 +12,44 @@ M.should_register = function(root, _)
if vim.loop.fs_stat(root .. "/project.yml") then
return true
end

return false
end

--- Register current neovim client
M.register = function()
local _ = lib.ensure()
lib.register { address = vim.env.NVIM_LISTEN_ADDRESS }
end

---Tries to register vim instance as client for xcodebase server.
---Only register the vim instance when `xcodebase.should_attach`
---@see xcodebase.should_attach
M.try_register = function(opts)
M.try_register = function(root, opts)
opts = opts or {}
local root = vim.loop.cwd()

if M.should_register(root, opts) then
local _ = lib.daemon.ensure()
lib.daemon.register(pid, root, address)
M.register()
vim.cmd [[ autocmd VimLeavePre * lua require'xcodebase'.drop()]]
else
return
end
end

M.drop = function()
lib.drop {}
end

M.build = function(opts)
local root = vim.loop.cwd()
lib.build(vim.tbl_extend("keep", opts or {}, {
pid = pid,
root = root,
}))
end

M.projects = {}

M.project_info = function(root)
M.projects[root] = nil
lib.project_info(pid, root)
while M.projects[root] == nil do
end
end

Expand All @@ -67,14 +58,13 @@ end
---@param opts XcodeBaseOptions
---@overload fun()
M.setup = function(opts)
local root = vim.loop.cwd()
opts = opts or {}

-- Mutate xcodebase configuration
config.set(opts)

-- Try to register current vim instance
-- NOTE: Should this register again on cwd change?
M.try_register(opts)
M.try_register(root, opts)
end

return M
25 changes: 17 additions & 8 deletions lua/xcodebase/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
use mlua::lua_module;
use mlua::prelude::*;
use xcodebase::daemon::Daemon;
use xcodebase::daemon::*;

#[lua_module]
#[mlua::lua_module]
fn libxcodebase(lua: &Lua) -> LuaResult<LuaTable> {
let commands = lua.create_table()?;
let module = lua.create_table()?;
module.set("commands", commands)?;
module.set("daemon", Daemon::lua(lua)?)?;
Ok(module)
let is_running = lua.create_function(Daemon::is_running)?;
let ensure = lua.create_function(Daemon::ensure)?;
let register = lua.create_function(Register::request)?;
let drop = lua.create_function(Drop::request)?;
let build = lua.create_function(Build::request)?;
let project_info = lua.create_function(ProjectInfo::request)?;

lua.create_table_from([
("is_running", is_running),
("ensure", ensure),
("register", register),
("drop", drop),
("build", build),
("project_info", project_info),
])
}
113 changes: 29 additions & 84 deletions src/daemon.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
//! Handle requests from neovim and manage dev workflow
#[cfg(feature = "daemon")]
mod nvim;
mod message;
mod requests;
pub mod state;

pub use requests::*;
mod client;
pub use client::Client;

#[cfg(feature = "daemon")]
pub use state::DaemonState;
mod nvim;

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

pub use message::*;
pub use requests::*;

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

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

#[cfg(feature = "lua")]
use std::{io::Write, net::Shutdown, os::unix::net::UnixStream, process::Command};

pub const DAEMON_SOCKET_PATH: &str = "/tmp/xcodebase-daemon.socket";
pub const DAEMON_BINARY: &str =
Expand All @@ -25,81 +31,21 @@ pub const DAEMON_BINARY: &str =
/// Representation of daemon
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>;
}

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

#[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,
DaemonRequest::ProjectInfo(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(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)?),
ProjectInfo::KEY => Self::ProjectInfo(ProjectInfo::parse(args)?),
cmd => anyhow::bail!("Unknown command messsage: {cmd}"),
})
}
}

#[cfg(feature = "lua")]
impl Daemon {
/// Representation of daemon table in lua
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)?)?;
table.set("register", lua.create_function(Register::lua)?)?;
table.set("drop", lua.create_function(Drop::lua)?)?;
table.set("build", lua.create_function(Build::lua)?)?;
table.set("project_info", lua.create_function(ProjectInfo::lua)?)?;
Ok(table)
}

/// Check if Daemon is running
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),
}
pub fn is_running(_: &Lua, _: ()) -> LuaResult<bool> {
Ok(match UnixStream::connect(DAEMON_SOCKET_PATH) {
Ok(stream) => stream.shutdown(Shutdown::Both).ok().is_some(),
Err(_) => false,
})
}

/// Ensure that daemon is currently running in background
pub fn ensure(lua: &mlua::Lua, _: ()) -> LuaResult<bool> {
pub fn ensure(lua: &Lua, _: ()) -> LuaResult<bool> {
if Self::is_running(lua, ()).unwrap() {
Ok(false)
} else if std::process::Command::new(DAEMON_BINARY).spawn().is_ok() {
} else if Command::new(DAEMON_BINARY).spawn().is_ok() {
lua.info("Spawned Background Server")?;
Ok(true)
} else {
Expand All @@ -108,17 +54,16 @@ impl Daemon {
}

/// 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
))),
}
pub fn execute<I: Into<Request> + std::fmt::Debug>(_lua: &Lua, message: I) -> LuaResult<()> {
let req: Request = message.into();
let mut stream = UnixStream::connect(DAEMON_SOCKET_PATH)
.map_err(|e| format!("Connect: {e} and execute: {:#?}", req))
.to_lua_err()?;

serde_json::to_vec(&req)
.map(|value| stream.write_all(&value))
.to_lua_err()??;

stream.flush().to_lua_err()
}
}
Loading

0 comments on commit e1ac069

Please sign in to comment.