1use crate::env::{
5 ArgumentsEnvironment, AsyncIoEnvironment, ChangeWorkingDirectoryEnvironment,
6 FileDescEnvironment, RedirectEnvRestorer, ShiftArgumentsEnvironment, StringWrapper,
7 SubEnvironment, VarEnvRestorer, VariableEnvironment,
8};
9use crate::spawn::builtin;
10use crate::ExitStatus;
11use futures_core::future::BoxFuture;
12use std::borrow::Borrow;
13use std::fmt;
14use std::marker::PhantomData;
15
16pub trait BuiltinUtility<'a, A, R, E>
24where
25 R: ?Sized,
26 E: 'a + ?Sized,
27{
28 fn spawn_builtin<'life0, 'life1, 'async_trait>(
40 &'life0 self,
41 args: A,
42 restorer: &'life1 mut R,
43 ) -> BoxFuture<'async_trait, BoxFuture<'static, ExitStatus>>
44 where
45 'life0: 'async_trait,
46 'life1: 'async_trait,
47 Self: 'async_trait,
48 A: 'async_trait;
49}
50
51impl<'a, A, R, E, T> BuiltinUtility<'a, A, R, E> for &'_ T
52where
53 R: ?Sized,
54 E: 'a + ?Sized,
55 T: BuiltinUtility<'a, A, R, E>,
56{
57 fn spawn_builtin<'life0, 'life1, 'async_trait>(
58 &'life0 self,
59 args: A,
60 restorer: &'life1 mut R,
61 ) -> BoxFuture<'async_trait, BoxFuture<'static, ExitStatus>>
62 where
63 'life0: 'async_trait,
64 'life1: 'async_trait,
65 Self: 'async_trait,
66 A: 'async_trait,
67 {
68 (**self).spawn_builtin(args, restorer)
69 }
70}
71
72pub trait BuiltinEnvironment {
74 type BuiltinName;
76 type Builtin;
78
79 fn builtin(&self, name: &Self::BuiltinName) -> Option<Self::Builtin>;
81}
82
83impl<'a, T: ?Sized + BuiltinEnvironment> BuiltinEnvironment for &'a T {
84 type BuiltinName = T::BuiltinName;
85 type Builtin = T::Builtin;
86
87 fn builtin(&self, name: &Self::BuiltinName) -> Option<Self::Builtin> {
88 (**self).builtin(name)
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93enum BuiltinKind {
94 Cd,
95 Colon,
96 Echo,
97 False,
98 Pwd,
99 Shift,
100 True,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub struct Builtin {
106 kind: BuiltinKind,
107}
108
109pub struct BuiltinEnv<T> {
111 phantom: PhantomData<fn(T)>,
112}
113
114impl<T> Eq for BuiltinEnv<T> {}
115impl<T> PartialEq<BuiltinEnv<T>> for BuiltinEnv<T> {
116 fn eq(&self, other: &BuiltinEnv<T>) -> bool {
117 self.phantom == other.phantom
118 }
119}
120
121impl<T> fmt::Debug for BuiltinEnv<T> {
122 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
123 fmt.debug_struct("BuiltinEnv").finish()
124 }
125}
126
127impl<T> Copy for BuiltinEnv<T> {}
128impl<T> Clone for BuiltinEnv<T> {
129 fn clone(&self) -> Self {
130 *self
131 }
132}
133
134impl<T> Default for BuiltinEnv<T> {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140impl<T> BuiltinEnv<T> {
141 pub fn new() -> Self {
143 Self {
144 phantom: PhantomData,
145 }
146 }
147}
148
149impl<T> SubEnvironment for BuiltinEnv<T> {
150 fn sub_env(&self) -> Self {
151 *self
152 }
153}
154
155fn lookup_builtin(name: &str) -> Option<BuiltinKind> {
156 match name {
157 "cd" => Some(BuiltinKind::Cd),
158 ":" => Some(BuiltinKind::Colon),
159 "echo" => Some(BuiltinKind::Echo),
160 "false" => Some(BuiltinKind::False),
161 "pwd" => Some(BuiltinKind::Pwd),
162 "shift" => Some(BuiltinKind::Shift),
163 "true" => Some(BuiltinKind::True),
164
165 _ => None,
166 }
167}
168
169impl<T> BuiltinEnvironment for BuiltinEnv<T>
170where
171 T: StringWrapper,
172{
173 type BuiltinName = T;
174 type Builtin = Builtin;
175
176 fn builtin(&self, name: &Self::BuiltinName) -> Option<Self::Builtin> {
177 lookup_builtin(name.as_str()).map(|kind| Builtin { kind })
178 }
179}
180
181impl<'a, A, R, E> BuiltinUtility<'a, A, R, E> for Builtin
182where
183 A: Send + IntoIterator,
184 A::Item: Send + StringWrapper,
185 A::IntoIter: Send,
186 R: ?Sized + Send + RedirectEnvRestorer<'a, E> + VarEnvRestorer<'a, E>,
187 E: 'a
188 + ?Sized
189 + Send
190 + Sync
191 + AsyncIoEnvironment
192 + ArgumentsEnvironment
193 + ChangeWorkingDirectoryEnvironment
194 + FileDescEnvironment
195 + VariableEnvironment
196 + ShiftArgumentsEnvironment,
197 E::FileHandle: Clone,
198 E::IoHandle: Send + From<E::FileHandle>,
199 E::Var: Borrow<String> + From<String>,
200 E::VarName: Borrow<String> + From<String>,
201{
202 fn spawn_builtin<'life0, 'life1, 'async_trait>(
203 &'life0 self,
204 args: A,
205 restorer: &'life1 mut R,
206 ) -> BoxFuture<'async_trait, BoxFuture<'static, ExitStatus>>
207 where
208 'life0: 'async_trait,
209 'life1: 'async_trait,
210 Self: 'async_trait,
211 A: 'async_trait,
212 {
213 let kind = self.kind;
214
215 Box::pin(async move {
216 let env = restorer.get_mut();
217
218 let ret = match kind {
219 BuiltinKind::Cd => builtin::cd(args, env).await,
220 BuiltinKind::Echo => builtin::echo(args, env).await,
221 BuiltinKind::Pwd => builtin::pwd(args, env).await,
222 BuiltinKind::Shift => builtin::shift(args, env).await,
223
224 BuiltinKind::Colon => Box::pin(async { builtin::colon() }),
225 BuiltinKind::False => Box::pin(async { builtin::false_cmd() }),
226 BuiltinKind::True => Box::pin(async { builtin::true_cmd() }),
227 };
228
229 restorer.restore_vars();
230 restorer.restore_redirects();
231
232 ret
233 })
234 }
235}