taiga_plugin_api/daemon/
ipc.rs1use interprocess::local_socket::tokio::Stream;
6use serde::{Deserialize, Serialize};
7use std::process::{Child, Command, Stdio};
8use crate::PluginError;
9use tokio::io::{AsyncReadExt, AsyncWriteExt};
10
11pub async fn send_message<T: Serialize>(
13 stream: &mut Stream,
14 msg: &T,
15) -> Result<(), PluginError> {
16 let bytes = serde_json::to_vec(msg)?;
17 stream.write_all(&bytes).await?;
18 Ok(())
19}
20
21pub async fn receive_message<T: for<'de> Deserialize<'de>>(
27 stream: &mut Stream,
28 buffer_size: usize,
29) -> Result<T, PluginError> {
30 let mut buffer = vec![0u8; buffer_size];
31 let n = stream.read(&mut buffer).await?;
32
33 if n == 0 {
34 return Err(PluginError::ipc_connection(
35 "Connection closed without response",
36 ));
37 }
38
39 let msg: T = serde_json::from_slice(&buffer[0..n])?;
40 Ok(msg)
41}
42
43#[derive(Debug, Clone)]
45pub struct DaemonSpawnConfig {
46 pub plugin_name: String,
48 pub daemon_command: String,
50 pub additional_args: Vec<String>,
52}
53
54impl DaemonSpawnConfig {
55 pub fn new(plugin_name: impl Into<String>, daemon_command: impl Into<String>) -> Self {
57 Self {
58 plugin_name: plugin_name.into(),
59 daemon_command: daemon_command.into(),
60 additional_args: Vec::new(),
61 }
62 }
63
64 pub fn with_args(mut self, args: Vec<String>) -> Self {
66 self.additional_args = args;
67 self
68 }
69}
70
71pub fn spawn_daemon_process(config: &DaemonSpawnConfig) -> Result<Child, std::io::Error> {
78 let current_exe = std::env::current_exe()?;
79
80 let mut args = vec![config.plugin_name.clone(), config.daemon_command.clone()];
81 args.extend(config.additional_args.clone());
82
83 Command::new(¤t_exe)
84 .args(&args)
85 .stdin(Stdio::null())
86 .stdout(Stdio::null())
87 .stderr(Stdio::null())
88 .spawn()
89 .map_err(|e| {
90 std::io::Error::new(
91 e.kind(),
92 format!(
93 "Failed to spawn daemon process '{}': {}",
94 current_exe.display(),
95 e
96 ),
97 )
98 })
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_daemon_spawn_config_creation() {
107 let config = DaemonSpawnConfig::new("test", "daemon");
108 assert_eq!(config.plugin_name, "test");
109 assert_eq!(config.daemon_command, "daemon");
110 assert!(config.additional_args.is_empty());
111 }
112
113 #[test]
114 fn test_daemon_spawn_config_with_args() {
115 let config = DaemonSpawnConfig::new("test", "daemon")
116 .with_args(vec!["--verbose".to_string()]);
117 assert_eq!(config.additional_args.len(), 1);
118 }
119}