use std::path::Path;
use std::process::{Command, Stdio};
use std::time::Duration;
use anyhow::{Result, bail};
use crate::proto::{self, Request};
use crate::transport::{self, Stream};
pub fn request_stream(root: &Path, req: &Request, sink: &mut impl std::io::Write) -> Result<usize> {
let mut stream = connect_or_spawn(root)?;
proto::write_request(&mut stream, req)?;
proto::read_stream(&mut stream, sink)
}
pub fn request(root: &Path, req: &Request) -> Result<Vec<u8>> {
let mut out = Vec::new();
request_stream(root, req, &mut out)?;
Ok(out)
}
pub fn request_existing(root: &Path, req: &Request) -> Result<Option<Vec<u8>>> {
match transport::connect(root)? {
Some(mut stream) => {
proto::write_request(&mut stream, req)?;
Ok(Some(proto::read_stream_to_vec(&mut stream)?))
}
None => Ok(None),
}
}
pub fn store_cursor(root: &Path, blob: Vec<u8>) -> Result<String> {
let out = request(root, &Request::CursorStore { blob })?;
Ok(String::from_utf8(out)?)
}
pub fn take_cursor(root: &Path, token: &str) -> Result<Option<Vec<u8>>> {
let reply = request_existing(
root,
&Request::CursorTake {
token: token.to_string(),
},
)?;
Ok(reply.filter(|blob| !blob.is_empty()))
}
pub fn watch(root: &Path, mut render: impl FnMut(&[u8])) -> Result<()> {
let mut stream = connect_or_spawn(root)?;
proto::write_request(&mut stream, &Request::Watch)?;
while let Some(frame) = proto::read_watch_frame(&mut stream)? {
render(&frame);
}
Ok(())
}
fn connect_or_spawn(root: &Path) -> Result<Stream> {
if let Some(s) = transport::connect(root)? {
return Ok(s);
}
spawn_daemon(root)?;
for _ in 0..400 {
std::thread::sleep(Duration::from_millis(25));
if let Some(s) = transport::connect(root)? {
return Ok(s);
}
}
bail!("daemon did not come up for {}", root.display());
}
pub fn wait_until_stopped(root: &Path) -> bool {
for _ in 0..200 {
if let Ok(None) = transport::connect(root) {
return true;
}
std::thread::sleep(Duration::from_millis(25));
}
false
}
pub fn spawn_daemon(root: &Path) -> Result<()> {
let exe = std::env::current_exe()?;
let mut cmd = Command::new(exe);
cmd.arg("--server")
.current_dir(root)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
detach(&mut cmd);
cmd.spawn()?;
Ok(())
}
#[cfg(unix)]
fn detach(cmd: &mut Command) {
use std::os::unix::process::CommandExt;
cmd.process_group(0);
}
#[cfg(windows)]
fn detach(cmd: &mut Command) {
use std::os::windows::process::CommandExt;
cmd.creation_flags(0x0000_0008 | 0x0000_0200 | 0x0800_0000);
}