hypershell_tokio_components/providers/
core_exec.rs1use 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}