From bfe6d20354463e5cf7b98ff2348b4e209b16d4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fe=CC=81lix=20Saparelli?= Date: Sun, 5 Nov 2023 00:46:03 +1300 Subject: [PATCH] Add ErasedChild --- CHANGELOG.md | 1 + src/stdlib.rs | 4 ++ src/stdlib/erased.rs | 79 ++++++++++++++++++++++++++++++++++++++ src/tokio.rs | 4 ++ src/tokio/erased.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 src/stdlib/erased.rs create mode 100644 src/tokio/erased.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 58c5144..c3b0f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Clarify why and in which situations `AsyncGroupChild::wait` may not behave as expected when cancelled. - Add `AsyncGroupChild::start_kill` to align with Tokio's `Child::start_kill`. - Change `AsyncGroupChild::kill` to also `wait()` on the child, to align with Tokio's `Child::kill`. +- Add `ErasedChild` abstraction to allow using the same type for grouped and ungrouped children. ## v3.0.0 (2023-10-30) diff --git a/src/stdlib.rs b/src/stdlib.rs index 1fd0221..9bafcab 100644 --- a/src/stdlib.rs +++ b/src/stdlib.rs @@ -8,6 +8,9 @@ use std::{ use crate::{builder::CommandGroupBuilder, GroupChild}; +#[doc(inline)] +pub use erased::ErasedChild; + #[cfg(target_family = "windows")] mod windows; @@ -15,6 +18,7 @@ mod windows; mod unix; pub(crate) mod child; +pub(crate) mod erased; /// Extensions for [`Command`](std::process::Command) adding support for process groups. pub trait CommandGroup { diff --git a/src/stdlib/erased.rs b/src/stdlib/erased.rs new file mode 100644 index 0000000..76ff229 --- /dev/null +++ b/src/stdlib/erased.rs @@ -0,0 +1,79 @@ +use std::{ + io::Result, + process::{Child, ExitStatus, Output}, +}; + +use super::GroupChild; + +/// Wrapper around a process child, be it grouped or ungrouped. +/// +/// This is a helper which erases that a [`std::process::Child`] is a different type than a +/// [`GroupChild`]. It forwards to the corresponding method on the inner type. +#[derive(Debug)] +pub enum ErasedChild { + /// A grouped process child. + Grouped(GroupChild), + + /// An ungrouped process child. + Ungrouped(Child), +} + +impl ErasedChild { + /// Forces the child to exit. + /// + /// - Grouped: [`GroupChild::kill`] + /// - Ungrouped: [`Child::kill`] + pub fn kill(&mut self) -> Result<()> { + match self { + Self::Grouped(c) => c.kill(), + Self::Ungrouped(c) => c.kill(), + } + } + + /// Attempts to collect the exit status of the child if it has already exited. + /// + /// - Grouped: [`GroupChild::try_wait`] + /// - Ungrouped: [`Child::try_wait`] + pub fn try_wait(&mut self) -> Result> { + match self { + Self::Grouped(c) => c.try_wait(), + Self::Ungrouped(c) => c.try_wait(), + } + } + + /// Waits for the process to exit, and returns its exit status. + /// + /// - Grouped: [`GroupChild::wait`] + /// - Ungrouped: [`Child::wait`] + pub fn wait(&mut self) -> Result { + match self { + Self::Grouped(c) => c.wait(), + Self::Ungrouped(c) => c.wait(), + } + } + + /// Waits for the process to exit, and returns its exit status. + /// + /// - Grouped: [`GroupChild::wait_with_output`] + /// - Ungrouped: [`Child::wait_with_output`] + pub fn wait_with_output(self) -> Result { + match self { + Self::Grouped(c) => c.wait_with_output(), + Self::Ungrouped(c) => c.wait_with_output(), + } + } + + /// Sends a Unix signal to the process. + /// + /// - Grouped: [`GroupChild::signal`] + /// - Ungrouped: [`Child::signal`] + #[cfg(unix)] + pub fn signal(&mut self, sig: crate::Signal) -> Result<()> { + use crate::UnixChildExt; + + match self { + Self::Grouped(c) => c.signal(sig), + Self::Ungrouped(c) => c.signal(sig), + } + } +} diff --git a/src/tokio.rs b/src/tokio.rs index 45dbfc4..695a893 100644 --- a/src/tokio.rs +++ b/src/tokio.rs @@ -10,6 +10,9 @@ use tokio::process::Command; use crate::{builder::CommandGroupBuilder, AsyncGroupChild}; +#[doc(inline)] +pub use erased::ErasedChild; + #[cfg(target_family = "windows")] mod windows; @@ -17,6 +20,7 @@ mod windows; mod unix; pub(crate) mod child; +pub(crate) mod erased; /// Extensions for [`Command`](::tokio::process::Command) adding support for process groups. /// diff --git a/src/tokio/erased.rs b/src/tokio/erased.rs new file mode 100644 index 0000000..baf682c --- /dev/null +++ b/src/tokio/erased.rs @@ -0,0 +1,91 @@ +use std::{ + io::Result, + process::{ExitStatus, Output}, +}; + +use super::AsyncGroupChild; +use tokio::process::Child; + +/// Wrapper around a process child, be it grouped or ungrouped. +/// +/// This is a helper which erases that a [`tokio::process::Child`] is a different type than an +/// [`AsyncGroupChild`]. It forwards to the corresponding method on the inner type. +#[derive(Debug)] +pub enum ErasedChild { + /// A grouped process child. + Grouped(AsyncGroupChild), + + /// An ungrouped process child. + Ungrouped(Child), +} + +impl ErasedChild { + /// Forces the child to exit. + /// + /// - Grouped: [`AsyncGroupChild::kill`] + /// - Ungrouped: [`Child::kill`] + pub async fn kill(&mut self) -> Result<()> { + match self { + Self::Grouped(c) => c.kill().await, + Self::Ungrouped(c) => c.kill().await, + } + } + + /// Attempts to force the child to exit, but does not wait for the request to take effect. + /// + /// - Grouped: [`AsyncGroupChild::start_kill`] + /// - Ungrouped: [`Child::start_kill`] + pub fn start_kill(&mut self) -> Result<()> { + match self { + Self::Grouped(c) => c.start_kill(), + Self::Ungrouped(c) => c.start_kill(), + } + } + + /// Attempts to collect the exit status of the child if it has already exited. + /// + /// - Grouped: [`AsyncGroupChild::try_wait`] + /// - Ungrouped: [`Child::try_wait`] + pub fn try_wait(&mut self) -> Result> { + match self { + Self::Grouped(c) => c.try_wait(), + Self::Ungrouped(c) => c.try_wait(), + } + } + + /// Waits for the process to exit, and returns its exit status. + /// + /// - Grouped: [`AsyncGroupChild::wait`] + /// - Ungrouped: [`Child::wait`] + pub async fn wait(&mut self) -> Result { + match self { + Self::Grouped(c) => c.wait().await, + Self::Ungrouped(c) => c.wait().await, + } + } + + /// Waits for the process to exit, and returns its exit status. + /// + /// - Grouped: [`AsyncGroupChild::wait_with_output`] + /// - Ungrouped: [`Child::wait_with_output`] + pub async fn wait_with_output(self) -> Result { + match self { + Self::Grouped(c) => c.wait_with_output().await, + Self::Ungrouped(c) => c.wait_with_output().await, + } + } + + /// Sends a Unix signal to the process. + /// + /// - Grouped: [`AsyncGroupChild::signal`] + /// - Ungrouped: [`Child::signal`] + #[cfg(unix)] + pub fn signal(&mut self, sig: crate::Signal) -> Result<()> { + use crate::UnixChildExt; + + match self { + Self::Grouped(c) => c.signal(sig), + Self::Ungrouped(c) => c.signal(sig), + } + } +}