use std::ffi::OsStr;
use std::fmt;
use std::os::windows::io::{AsHandle, BorrowedHandle};
use std::path::Path;
use crate::core::child::ChildCore;
use crate::core::session;
use crate::core::wait::RegisteredWait;
use crate::error::{Error, Result};
use crate::status::ExitStatus;
use crate::SessionOptions;
use super::pty::Pty;
use super::session::Session;
#[derive(Debug)]
pub struct Command {
inner: crate::command::Command,
}
impl Command {
#[must_use]
pub fn new(program: impl AsRef<OsStr>) -> Self {
Self {
inner: crate::command::Command::new(program),
}
}
pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
self.inner.arg(arg);
self
}
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
self.inner.args(args);
self
}
pub fn raw_arg(&mut self, text: impl AsRef<OsStr>) -> &mut Self {
self.inner.raw_arg(text);
self
}
pub fn env(&mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> &mut Self {
self.inner.env(key, value);
self
}
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.inner.envs(vars);
self
}
pub fn env_remove(&mut self, key: impl AsRef<OsStr>) -> &mut Self {
self.inner.env_remove(key);
self
}
pub fn env_clear(&mut self) -> &mut Self {
self.inner.env_clear();
self
}
pub fn current_dir(&mut self, dir: impl AsRef<Path>) -> &mut Self {
self.inner.current_dir(dir);
self
}
#[cfg(test)]
pub(crate) fn kill_on_drop(&mut self, kill: bool) -> &mut Self {
self.inner.kill_on_drop(kill);
self
}
pub fn spawn(&mut self) -> Result<Session> {
self.spawn_with(SessionOptions::default())
}
pub fn spawn_with(&mut self, options: SessionOptions) -> Result<Session> {
let (size, backend) = options.into_parts();
let mut builder = Pty::builder().size(size);
if let Some(backend) = backend {
builder = builder.backend(backend);
}
let pty = builder.build()?;
let child = self.spawn_in_with_policy(&pty, true)?;
let controller = pty.controller();
let (output, input) = pty.into_split();
Ok(Session {
child,
output,
input,
controller,
})
}
#[cfg(test)]
pub(crate) fn spawn_in(&mut self, pty: &Pty) -> Result<Child> {
self.spawn_in_with_policy(pty, self.inner.get_kill_on_drop())
}
fn spawn_in_with_policy(&self, pty: &Pty, kill_on_drop: bool) -> Result<Child> {
let root = session::spawn_root(&pty.inner, &self.inner, kill_on_drop)?;
Ok(Child {
core: ChildCore::from_root(root),
exit: None,
})
}
}
pub struct Child {
core: ChildCore,
exit: Option<RegisteredWait>,
}
impl fmt::Debug for Child {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.core.fmt(f)
}
}
impl Child {
#[must_use]
pub const fn id(&self) -> u32 {
self.core.id()
}
pub async fn wait(&mut self) -> Result<ExitStatus> {
if let Some(status) = self.core.status() {
return Ok(status);
}
if self.exit.is_none() {
self.exit = Some(RegisteredWait::new(self.core.as_handle()).map_err(Error::wait)?);
}
let wait = self.exit.as_mut().ok_or_else(|| {
Error::wait(std::io::Error::other(
"the registered process wait was not initialized",
))
})?;
let result = wait.await;
self.exit = None;
let code = result.map_err(Error::wait)?;
Ok(self.core.cache_exit_code(code))
}
pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
let status = self.core.try_wait()?;
if status.is_some() {
self.exit = None;
}
Ok(status)
}
#[cfg(test)]
pub(crate) const fn cached_status(&self) -> Option<ExitStatus> {
self.core.status()
}
pub fn kill(&mut self) -> Result<()> {
self.core.kill()
}
}
impl AsHandle for Child {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.core.as_handle()
}
}