claude_code_acp/session/wrapped_child.rs
1//! Wrapped child process with process group support
2//!
3//! Provides a unified interface over process-wrap's ChildWrapper
4//! while maintaining compatibility with existing Arc<Mutex<>> pattern.
5
6use process_wrap::tokio::ChildWrapper;
7use std::io;
8use std::pin::Pin;
9
10/// Wrapper around Box<dyn ChildWrapper> that provides
11/// a stable interface compatible with Arc<Mutex<>>
12///
13/// This wraps the process-wrap ChildWrapper trait to work
14/// with our existing Arc<Mutex<>> storage pattern in BackgroundTerminal.
15#[derive(Debug)]
16pub struct WrappedChild {
17 inner: Box<dyn ChildWrapper>,
18}
19
20impl WrappedChild {
21 /// Create a new wrapped child from a process-wrap ChildWrapper
22 pub fn new(inner: Box<dyn ChildWrapper>) -> Self {
23 Self { inner }
24 }
25
26 /// Get mutable reference to inner ChildWrapper
27 pub fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
28 self.inner.as_mut()
29 }
30
31 /// Kill the process group and wait for exit
32 ///
33 /// This will terminate the entire process group, not just the parent process.
34 pub async fn kill(&mut self) -> io::Result<()> {
35 Pin::from(self.inner.kill()).await
36 }
37
38 /// Start killing without waiting for exit
39 ///
40 /// This initiates the kill operation but returns immediately.
41 pub fn start_kill(&mut self) -> io::Result<()> {
42 self.inner.start_kill()
43 }
44
45 /// Wait for the process to exit
46 pub async fn wait(&mut self) -> io::Result<std::process::ExitStatus> {
47 Pin::from(self.inner.wait()).await
48 }
49
50 /// Try to wait without blocking
51 ///
52 /// Returns Some(status) if the process has exited, None if still running.
53 pub fn try_wait(&mut self) -> io::Result<Option<std::process::ExitStatus>> {
54 self.inner.try_wait()
55 }
56
57 /// Send a specific signal to the process group (Unix only)
58 ///
59 /// # Arguments
60 /// * `sig` - Signal number (e.g., libc::SIGTERM, libc::SIGKILL)
61 #[cfg(unix)]
62 pub fn signal(&self, sig: i32) -> io::Result<()> {
63 self.inner.signal(sig)
64 }
65
66 /// Get the process ID
67 pub fn id(&self) -> u32 {
68 self.inner.id().unwrap_or(0)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 #[test]
75 fn test_wrapped_child_creation() {
76 // This is a placeholder test
77 // Real tests would require spawning an actual process
78 // which is better done as integration tests
79 }
80}