use std::io::Read;
use std::process::Stdio;
use tokio::io;
use tokio::process::{Child, Command};
pub struct Rigctld {
daemon: Option<Child>,
}
impl Rigctld {
pub fn new(child: Child) -> Self {
Self {
daemon: Some(child),
}
}
pub async fn kill(&mut self) -> Result<(), io::Error> {
let res = if let Some(d) = self.daemon.as_mut() {
d.kill().await
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"Daemon not started",
))
};
self.daemon = None;
res
}
pub fn is_running(&mut self) -> Result<bool, io::Error> {
if let Some(d) = self.daemon.as_mut() {
match d.try_wait()? {
Some(_) => {
self.daemon = None;
Ok(false)
}
None => Ok(true),
}
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"Daemon not started",
))
}
}
}
#[derive(Debug)]
pub struct Daemon {
program: String,
host: String,
port: u16,
model: u32,
rig_file: Option<String>,
serial_speed: Option<u32>,
civ_address: Option<u16>,
}
impl Default for Daemon {
fn default() -> Self {
Daemon {
program: "rigctld".into(),
host: "127.0.0.1".into(),
port: 4532,
model: 1,
rig_file: None,
serial_speed: None,
civ_address: None,
}
}
}
impl Daemon {
pub async fn spawn(&self) -> Result<Rigctld, io::Error> {
let mut binding = Command::new(self.program.clone());
binding.kill_on_drop(true);
let cmd = binding
.args(["-T", &self.host])
.args(["-t", &self.port.to_string()])
.args(["-m", &self.model.to_string()]);
if let Some(rig) = self.rig_file.as_ref() {
cmd.args(["-r", rig]);
}
if let Some(speed) = self.serial_speed.as_ref() {
cmd.args(["-s", &speed.to_string()]);
}
if let Some(civ) = self.civ_address.as_ref() {
cmd.args(["-c", &civ.to_string()]);
}
let daemon = Rigctld::new(cmd.spawn()?);
Ok(daemon)
}
pub async fn get_version(&self) -> Result<String, io::Error> {
let child = Command::new(self.program.clone())
.stdout(Stdio::piped())
.arg("--version")
.spawn()?
.wait_with_output()
.await?;
let mut version: String = String::new();
child.stdout.as_slice().read_to_string(&mut version)?;
version = String::from(version.trim_end());
Ok(version)
}
pub fn set_program(mut self, app: String) -> Daemon {
self.program = app;
self
}
pub fn set_host(mut self, host: String) -> Daemon {
self.host = host;
self
}
pub fn get_host(&self) -> &str {
&self.host
}
pub fn set_port(mut self, port: u16) -> Daemon {
self.port = port;
self
}
pub fn get_port(&self) -> u16 {
self.port
}
pub fn set_model(mut self, model: u32) -> Daemon {
self.model = model;
self
}
pub fn set_rig_file(mut self, file: String) -> Daemon {
self.rig_file = Some(file);
self
}
pub fn set_serial_speed(mut self, speed: u32) -> Daemon {
self.serial_speed = Some(speed);
self
}
pub fn set_civ_address(mut self, addr: u16) -> Daemon {
self.civ_address = Some(addr);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::runtime::Runtime;
macro_rules! tokio {
($e:expr) => {
Runtime::new().unwrap().block_on(async { $e })
};
}
#[test]
fn rigctld_exists() {
tokio!({
Daemon::default().spawn().await.unwrap();
})
}
#[test]
fn rigctld_version() {
tokio!({
Daemon::default().get_version().await.unwrap();
})
}
#[test]
fn daemon_lifecycle() {
tokio!({
let mut d = Daemon::default().spawn().await.unwrap();
assert_eq!(d.is_running().unwrap(), true);
d.kill().await.unwrap();
})
}
#[test]
fn daemon_kill_twice() {
tokio!({
let mut d = Daemon::default().spawn().await.unwrap();
d.kill().await.unwrap();
assert_eq!(d.kill().await.is_err(), true);
})
}
}