blob: 9060902bc3d20c5e2c7c7d87510e02bdde713305 [file] [log] [blame] [edit]
use super::CommandEnvs;
use super::env::CommandEnv;
use crate::ffi::OsStr;
pub use crate::ffi::OsString as EnvKey;
use crate::num::NonZeroI32;
use crate::os::fd::{FromRawFd, IntoRawFd};
use crate::os::motor::ffi::OsStrExt;
use crate::path::Path;
use crate::process::StdioPipes;
use crate::sys::fs::File;
use crate::sys::map_motor_error;
use crate::sys::pipe::AnonPipe;
use crate::sys_common::{AsInner, FromInner};
use crate::{fmt, io};
pub enum Stdio {
Inherit,
Null,
MakePipe,
Fd(crate::sys::fd::FileDesc),
}
impl Stdio {
fn into_rt(self) -> moto_rt::RtFd {
match self {
Stdio::Inherit => moto_rt::process::STDIO_INHERIT,
Stdio::Null => moto_rt::process::STDIO_NULL,
Stdio::MakePipe => moto_rt::process::STDIO_MAKE_PIPE,
Stdio::Fd(fd) => fd.into_raw_fd(),
}
}
fn try_clone(&self) -> io::Result<Self> {
match self {
Self::Fd(fd) => {
Ok(Self::Fd(crate::sys::fd::FileDesc::from_inner(fd.as_inner().try_clone()?)))
}
Self::Inherit => Ok(Self::Inherit),
Self::Null => Ok(Self::Null),
Self::MakePipe => Ok(Self::MakePipe),
}
}
}
#[derive(Default)]
pub struct Command {
program: String,
args: Vec<String>,
cwd: Option<String>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
env: CommandEnv,
}
impl Command {
pub fn new(program: &OsStr) -> Command {
let mut env = CommandEnv::default();
env.remove(OsStr::new(moto_rt::process::STDIO_IS_TERMINAL_ENV_KEY));
Command { program: program.as_str().to_owned(), env, ..Default::default() }
}
pub fn arg(&mut self, arg: &OsStr) {
self.args.push(arg.as_str().to_owned())
}
pub fn env_mut(&mut self) -> &mut CommandEnv {
&mut self.env
}
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(dir.as_str().to_owned())
}
pub fn stdin(&mut self, stdin: Stdio) {
self.stdin = Some(stdin);
}
pub fn stdout(&mut self, stdout: Stdio) {
self.stdout = Some(stdout);
}
pub fn stderr(&mut self, stderr: Stdio) {
self.stderr = Some(stderr);
}
pub fn get_program(&self) -> &OsStr {
OsStr::new(self.program.as_str())
}
pub fn get_args(&self) -> CommandArgs<'_> {
let iter = self.args.iter();
CommandArgs { iter }
}
pub fn get_envs(&self) -> CommandEnvs<'_> {
self.env.iter()
}
pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(Path::new)
}
pub fn spawn(
&mut self,
default: Stdio,
needs_stdin: bool,
) -> io::Result<(Process, StdioPipes)> {
let stdin = if let Some(stdin) = self.stdin.as_ref() {
stdin.try_clone()?.into_rt()
} else if needs_stdin {
default.try_clone()?.into_rt()
} else {
Stdio::Null.into_rt()
};
let stdout = if let Some(stdout) = self.stdout.as_ref() {
stdout.try_clone()?.into_rt()
} else {
default.try_clone()?.into_rt()
};
let stderr = if let Some(stderr) = self.stdout.as_ref() {
stderr.try_clone()?.into_rt()
} else {
default.try_clone()?.into_rt()
};
let mut env = Vec::<(String, String)>::new();
for (k, v) in self.env.capture() {
env.push((k.as_str().to_owned(), v.as_str().to_owned()));
}
let args = moto_rt::process::SpawnArgs {
program: self.program.clone(),
args: self.args.clone(),
env,
cwd: self.cwd.clone(),
stdin,
stdout,
stderr,
};
let (handle, stdin, stdout, stderr) =
moto_rt::process::spawn(args).map_err(map_motor_error)?;
Ok((
Process { handle },
StdioPipes {
stdin: if stdin >= 0 { Some(stdin.into()) } else { None },
stdout: if stdout >= 0 { Some(stdout.into()) } else { None },
stderr: if stderr >= 0 { Some(stderr.into()) } else { None },
},
))
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
unsafe { Stdio::Fd(crate::sys::fd::FileDesc::from_raw_fd(pipe.into_raw_fd())) }
}
}
impl From<crate::sys::fd::FileDesc> for Stdio {
fn from(fd: crate::sys::fd::FileDesc) -> Stdio {
Stdio::Fd(fd)
}
}
impl From<File> for Stdio {
fn from(_file: File) -> Stdio {
panic!("Not implemented")
}
}
impl From<io::Stdout> for Stdio {
fn from(_: io::Stdout) -> Stdio {
panic!("Not implemented")
}
}
impl From<io::Stderr> for Stdio {
fn from(_: io::Stderr) -> Stdio {
panic!("Not implemented")
}
}
impl fmt::Debug for Command {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct ExitStatus(i32);
impl ExitStatus {
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
if self.0 == 0 { Ok(()) } else { Err(ExitStatusError(*self)) }
}
pub fn code(&self) -> Option<i32> {
Some(self.0)
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "exit code: {}", self.0)
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitStatusError(ExitStatus);
impl Into<ExitStatus> for ExitStatusError {
fn into(self) -> ExitStatus {
self.0
}
}
impl ExitStatusError {
pub fn code(self) -> Option<NonZeroI32> {
NonZeroI32::new(self.0.0)
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitCode(i32);
impl ExitCode {
pub const SUCCESS: ExitCode = ExitCode(0);
pub const FAILURE: ExitCode = ExitCode(1);
pub fn as_i32(&self) -> i32 {
self.0
}
}
impl From<u8> for ExitCode {
fn from(code: u8) -> Self {
Self(code as i32)
}
}
pub struct Process {
handle: u64,
}
impl Drop for Process {
fn drop(&mut self) {
moto_rt::alloc::release_handle(self.handle).unwrap();
}
}
impl Process {
pub fn id(&self) -> u32 {
0
}
pub fn kill(&mut self) -> io::Result<()> {
match moto_rt::process::kill(self.handle) {
moto_rt::E_OK => Ok(()),
err => Err(map_motor_error(err)),
}
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
moto_rt::process::wait(self.handle).map(|c| ExitStatus(c)).map_err(map_motor_error)
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
match moto_rt::process::try_wait(self.handle) {
Ok(s) => Ok(Some(ExitStatus(s))),
Err(err) => match err {
moto_rt::E_NOT_READY => Ok(None),
err => Err(map_motor_error(err)),
},
}
}
#[allow(unused)]
pub fn handle(&self) -> u64 {
self.handle
}
}
pub struct CommandArgs<'a> {
iter: crate::slice::Iter<'a, String>,
}
impl<'a> Iterator for CommandArgs<'a> {
type Item = &'a OsStr;
fn next(&mut self) -> Option<&'a OsStr> {
self.iter.next().map(|arg| OsStr::new(arg))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a> ExactSizeIterator for CommandArgs<'a> {
fn len(&self) -> usize {
self.iter.len()
}
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}
impl<'a> fmt::Debug for CommandArgs<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter.clone()).finish()
}
}