Skip to main content

hypershell_tokio_components/providers/
core_exec.rs

1use core::fmt::Debug;
2use core::marker::PhantomData;
3use std::ffi::OsStr;
4use std::io::ErrorKind;
5use std::process::Stdio;
6
7use cgp::extra::handler::{Handler, HandlerComponent};
8use cgp::prelude::*;
9use hypershell_components::components::CanExtractCommandArg;
10use itertools::Itertools;
11use tokio::process::{Child, Command};
12
13use crate::components::CanUpdateCommand;
14use crate::dsl::CoreExec;
15
16#[cgp_new_provider]
17impl<Context, CommandPath, Args> Handler<Context, CoreExec<CommandPath, Args>, ()>
18    for HandleCoreExec
19where
20    Context: HasAsyncErrorType
21        + CanExtractCommandArg<CommandPath>
22        + CanUpdateCommand<Args>
23        + CanRaiseAsyncError<std::io::Error>
24        + for<'a> CanWrapAsyncError<CommandNotFound<'a>>
25        + for<'a> CanWrapAsyncError<SpawnCommandFailure<'a>>,
26    Context::CommandArg: AsRef<OsStr> + Send,
27    CommandPath: Send,
28    Args: Send,
29{
30    type Output = Child;
31
32    async fn handle(
33        context: &Context,
34        _tag: PhantomData<CoreExec<CommandPath, Args>>,
35        _input: (),
36    ) -> Result<Child, Context::Error> {
37        let command_path = context.extract_command_arg(PhantomData);
38
39        let mut command = Command::new(&command_path);
40
41        context.update_command(PhantomData, &mut command);
42
43        command.stdin(Stdio::piped());
44        command.stdout(Stdio::piped());
45        command.stderr(Stdio::piped());
46
47        let spawn_res = command.spawn();
48
49        let child = spawn_res.map_err(|e| {
50            let is_not_found = e.kind() == ErrorKind::NotFound;
51
52            let mut e = Context::raise_error(e);
53
54            if is_not_found {
55                e = Context::wrap_error(e, CommandNotFound { command: &command });
56            }
57
58            Context::wrap_error(e, SpawnCommandFailure { command: &command })
59        })?;
60
61        Ok(child)
62    }
63}
64
65pub struct SpawnCommandFailure<'a> {
66    pub command: &'a Command,
67}
68
69pub struct CommandNotFound<'a> {
70    pub command: &'a Command,
71}
72
73impl<'a> Debug for CommandNotFound<'a> {
74    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75        write!(
76            f,
77            "command not found: {}",
78            self.command.as_std().get_program().to_string_lossy(),
79        )
80    }
81}
82
83impl<'a> Debug for SpawnCommandFailure<'a> {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        write!(
86            f,
87            "error executing command: {} {}",
88            self.command.as_std().get_program().to_string_lossy(),
89            self.command
90                .as_std()
91                .get_args()
92                .map(|arg| arg.to_string_lossy())
93                .join(" "),
94        )
95    }
96}