conch_runtime_pshaw/spawn/
builtin.rs

1//! Defines methods for spawning shell builtin commands
2
3use crate::env::{AsyncIoEnvironment, FileDescEnvironment};
4use crate::{ExitStatus, Fd, EXIT_ERROR, EXIT_SUCCESS, STDERR_FILENO, STDOUT_FILENO};
5use futures_util::future::BoxFuture;
6use std::fmt;
7use void::Void;
8
9macro_rules! format_err {
10    ($builtin_name:expr, $e:expr) => {
11        format!("{}: {}\n", $builtin_name, $e).into_bytes()
12    };
13}
14
15macro_rules! try_and_report {
16    ($builtin_name:expr, $result:expr, $env:ident) => {
17        match $result {
18            Ok(val) => val,
19            Err(e) => {
20                return $crate::spawn::builtin::report_err($builtin_name, $env, e).await;
21            }
22        }
23    };
24}
25
26pub(crate) async fn report_err<E, ERR>(
27    builtin_name: &str,
28    env: &mut E,
29    err: ERR,
30) -> BoxFuture<'static, ExitStatus>
31where
32    E: ?Sized + AsyncIoEnvironment + FileDescEnvironment,
33    E::FileHandle: Clone,
34    E::IoHandle: From<E::FileHandle>,
35    ERR: fmt::Display,
36{
37    generate_and_write_bytes_to_fd_if_present(
38        builtin_name,
39        env,
40        STDERR_FILENO,
41        EXIT_ERROR,
42        |_| -> Result<_, Void> { Ok(format_err!(builtin_name, err)) },
43    )
44    .await
45}
46
47mod cd;
48mod echo;
49mod pwd;
50mod shift;
51mod trivial;
52
53pub use self::cd::cd;
54pub use self::echo::echo;
55pub use self::pwd::pwd;
56pub use self::shift::shift;
57pub use self::trivial::{colon, false_cmd, true_cmd};
58
59pub(crate) async fn generate_and_print_output<E, F, ERR>(
60    builtin_name: &str,
61    env: &mut E,
62    generate_bytes: F,
63) -> BoxFuture<'static, ExitStatus>
64where
65    E: ?Sized + AsyncIoEnvironment + FileDescEnvironment,
66    E::FileHandle: Clone,
67    E::IoHandle: From<E::FileHandle>,
68    for<'a> F: FnOnce(&'a E) -> Result<Vec<u8>, ERR>,
69    ERR: fmt::Display,
70{
71    generate_and_write_bytes_to_fd_if_present(
72        builtin_name,
73        env,
74        STDOUT_FILENO,
75        EXIT_SUCCESS,
76        generate_bytes,
77    )
78    .await
79}
80
81pub(crate) async fn generate_and_write_bytes_to_fd_if_present<E, F, ERR>(
82    builtin_name: &str,
83    env: &mut E,
84    fd: Fd,
85    exit_status_on_success: ExitStatus,
86    generate_bytes: F,
87) -> BoxFuture<'static, ExitStatus>
88where
89    E: ?Sized + AsyncIoEnvironment + FileDescEnvironment,
90    E::FileHandle: Clone,
91    E::IoHandle: From<E::FileHandle>,
92    for<'a> F: FnOnce(&'a E) -> Result<Vec<u8>, ERR>,
93    ERR: fmt::Display,
94{
95    macro_rules! get_fdes {
96        ($fd:expr, $fallback_status:expr) => {{
97            match get_fdes_or_status(env, fd, exit_status_on_success) {
98                Ok(fdes) => fdes,
99                Err(status) => return Box::pin(async move { status }),
100            }
101        }};
102    }
103
104    // If required handle is closed, just exit without doing more work
105    let fdes = get_fdes!(fd, exit_status_on_success);
106
107    let bytes_result = match generate_bytes(env) {
108        Ok(bytes) => Ok(bytes),
109        // If the caller already wants us to write data to stderr,
110        // we've already got a handle to it we can just proceed.
111        Err(e) if fd == STDERR_FILENO => Ok(format_err!(builtin_name, e)),
112        Err(e) => Err(e),
113    };
114
115    let err_bytes = match bytes_result {
116        Ok(bytes) => match env.write_all(fdes, bytes.into()).await {
117            Ok(()) => return Box::pin(async move { exit_status_on_success }),
118            Err(e) => format_err!(builtin_name, e),
119        },
120        Err(e) => format_err!(builtin_name, e),
121    };
122
123    // If we need to get a handle to stderr but it's closed, we bail out
124    let stderr_fdes = get_fdes!(fd, EXIT_ERROR);
125
126    let future = env.write_all(stderr_fdes, err_bytes.into());
127
128    Box::pin(async move {
129        // FIXME: debug log errors here?
130        let _ = future.await;
131        EXIT_ERROR
132    })
133}
134
135fn get_fdes_or_status<E>(
136    env: &E,
137    fd: Fd,
138    fallback_status: ExitStatus,
139) -> Result<E::IoHandle, ExitStatus>
140where
141    E: ?Sized + AsyncIoEnvironment + FileDescEnvironment,
142    E::FileHandle: Clone,
143    E::IoHandle: From<E::FileHandle>,
144{
145    env.file_desc(fd)
146        .map(|(fdes, _)| E::IoHandle::from(fdes.clone()))
147        .ok_or(fallback_status)
148}