summaryrefslogtreecommitdiffstats
path: root/src/builtins.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-03 01:19:02 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-03 01:46:28 -0500
commit234d71241399ae40d498d7953e9516f5cc5a471c (patch)
tree12ff8e2e983a7b4ffdf082780ab3cd473dbf86ac /src/builtins.rs
parent0115a566afa763c9732e03765d50a2ace4008d18 (diff)
downloadnbsh-234d71241399ae40d498d7953e9516f5cc5a471c.tar.gz
nbsh-234d71241399ae40d498d7953e9516f5cc5a471c.zip
start adding back builtin support
Diffstat (limited to 'src/builtins.rs')
-rw-r--r--src/builtins.rs285
1 files changed, 145 insertions, 140 deletions
diff --git a/src/builtins.rs b/src/builtins.rs
index e55f6ec..014eadd 100644
--- a/src/builtins.rs
+++ b/src/builtins.rs
@@ -1,79 +1,43 @@
use async_std::io::WriteExt as _;
use std::os::unix::process::ExitStatusExt as _;
-type BuiltinFunc = &'static (dyn for<'a> Fn(
- &'a crate::parse::Exe,
- &'a crate::command::Env,
-) -> std::pin::Pin<
- Box<
- dyn std::future::Future<Output = std::process::ExitStatus>
- + Sync
- + Send
- + 'a,
- >,
-> + Sync
+type Builtin = &'static (dyn Fn(
+ &crate::parse::Exe,
+ &crate::command::Env,
+) -> anyhow::Result<Child>
+ + Sync
+ Send);
+#[allow(clippy::as_conversions)]
static BUILTINS: once_cell::sync::Lazy<
- std::collections::HashMap<&'static str, BuiltinFunc>,
+ std::collections::HashMap<&'static str, Builtin>,
> = once_cell::sync::Lazy::new(|| {
- // all this does is convince the type system to do the right thing, i
- // don't think there's any way to just do it directly through annotations
- // or casts or whatever
- fn coerce_builtin<F>(f: &'static F) -> BuiltinFunc
- where
- F: for<'a> Fn(
- &'a crate::parse::Exe,
- &'a crate::command::Env,
- ) -> std::pin::Pin<
- Box<
- dyn std::future::Future<Output = std::process::ExitStatus>
- + Sync
- + Send
- + 'a,
- >,
- > + Sync
- + Send
- + 'static,
- {
- f
- }
-
let mut builtins = std::collections::HashMap::new();
- builtins.insert("cd", coerce_builtin(&|exe, env| Box::pin(cd(exe, env))));
- builtins
- .insert("and", coerce_builtin(&|exe, env| Box::pin(and(exe, env))));
- builtins.insert("or", coerce_builtin(&|exe, env| Box::pin(or(exe, env))));
- builtins.insert(
- "command",
- coerce_builtin(&|exe, env| Box::pin(command(exe, env))),
- );
- builtins.insert(
- "builtin",
- coerce_builtin(&|exe, env| Box::pin(builtin(exe, env))),
- );
+ builtins.insert("cd", &cd as Builtin);
+ builtins.insert("and", &and);
+ builtins.insert("or", &or);
+ builtins.insert("command", &command);
+ builtins.insert("builtin", &builtin);
builtins
});
-pub struct Builtin {
- f: BuiltinFunc,
+pub struct Command {
+ exe: crate::parse::Exe,
+ f: Builtin,
stdin: Box<dyn async_std::io::Read>,
stdout: Box<dyn async_std::io::Write>,
stderr: Box<dyn async_std::io::Write>,
}
-impl Builtin {
+impl Command {
pub fn new(exe: &crate::parse::Exe) -> Option<Self> {
- if let Some(f) = BUILTINS.get(exe.exe()) {
- Some(Self {
- f,
- stdin: Box::new(async_std::io::stdin()),
- stdout: Box::new(async_std::io::stdout()),
- stderr: Box::new(async_std::io::stderr()),
- })
- } else {
- None
- }
+ BUILTINS.get(exe.exe()).map(|f| Self {
+ exe: exe.clone(),
+ f,
+ stdin: Box::new(async_std::io::stdin()),
+ stdout: Box::new(async_std::io::stdout()),
+ stderr: Box::new(async_std::io::stderr()),
+ })
}
pub fn stdin(&mut self, fh: std::fs::File) {
@@ -87,128 +51,169 @@ impl Builtin {
pub fn stderr(&mut self, fh: std::fs::File) {
self.stderr = Box::new(async_std::fs::File::from(fh));
}
+
+ pub fn spawn(self, env: &crate::command::Env) -> anyhow::Result<Child> {
+ (self.f)(&self.exe, env)
+ }
}
-pub fn run(
- exe: &crate::parse::Exe,
- env: &crate::command::Env,
-) -> Option<
- std::pin::Pin<
+pub struct Child {
+ fut: std::pin::Pin<
Box<
- dyn std::future::Future<Output = async_std::process::ExitStatus>
- + Send
- + Sync,
+ dyn std::future::Future<Output = std::process::ExitStatus>
+ + Sync
+ + Send,
>,
>,
-> {
- // the closure form doesn't work without explicit type annotations
- #[allow(clippy::option_if_let_else)]
- if let Some(f) = BUILTINS.get(exe.exe()) {
- let exe = exe.clone();
- let env = env.clone();
- Some(Box::pin(async move { f(&exe, &env).await }))
- } else {
- None
+ wrapped_child: Option<Box<crate::command::Child>>,
+}
+
+impl Child {
+ pub fn id(&self) -> Option<u32> {
+ self.wrapped_child.as_ref().and_then(|cmd| cmd.id())
+ }
+
+ #[async_recursion::async_recursion]
+ pub async fn status(
+ self,
+ ) -> anyhow::Result<async_std::process::ExitStatus> {
+ if let Some(child) = self.wrapped_child {
+ child.status().await
+ } else {
+ Ok(self.fut.await)
+ }
}
}
-async fn cd(
+fn cd(
exe: &crate::parse::Exe,
env: &crate::command::Env,
-) -> async_std::process::ExitStatus {
- let dir = exe
- .args()
- .into_iter()
- .map(std::convert::AsRef::as_ref)
- .next()
- .unwrap_or("");
-
- let dir = if dir.is_empty() {
- home()
- } else if dir.starts_with('~') {
- let path: std::path::PathBuf = dir.into();
- if let std::path::Component::Normal(prefix) =
- path.components().next().unwrap()
- {
- if prefix.to_str() == Some("~") {
- home().join(path.strip_prefix(prefix).unwrap())
+) -> anyhow::Result<Child> {
+ async fn async_cd(
+ exe: &crate::parse::Exe,
+ _env: &crate::command::Env,
+ ) -> std::process::ExitStatus {
+ let dir = exe
+ .args()
+ .into_iter()
+ .map(std::convert::AsRef::as_ref)
+ .next()
+ .unwrap_or("");
+
+ let dir = if dir.is_empty() {
+ home()
+ } else if dir.starts_with('~') {
+ let path: std::path::PathBuf = dir.into();
+ if let std::path::Component::Normal(prefix) =
+ path.components().next().unwrap()
+ {
+ if prefix.to_str() == Some("~") {
+ home().join(path.strip_prefix(prefix).unwrap())
+ } else {
+ // TODO
+ async_std::io::stderr()
+ .write(b"unimplemented\n")
+ .await
+ .unwrap();
+ return async_std::process::ExitStatus::from_raw(1 << 8);
+ }
} else {
- // TODO
+ unreachable!()
+ }
+ } else {
+ dir.into()
+ };
+ let code = match std::env::set_current_dir(&dir) {
+ Ok(()) => 0,
+ Err(e) => {
async_std::io::stderr()
- .write(b"unimplemented\n")
+ .write(
+ format!(
+ "{}: {}: {}\n",
+ exe.exe(),
+ crate::format::io_error(&e),
+ dir.display()
+ )
+ .as_bytes(),
+ )
.await
.unwrap();
- return async_std::process::ExitStatus::from_raw(1 << 8);
+ 1
}
- } else {
- unreachable!()
- }
- } else {
- dir.into()
- };
- let code = match std::env::set_current_dir(&dir) {
- Ok(()) => 0,
- Err(e) => {
- async_std::io::stderr()
- .write(
- format!(
- "{}: {}: {}\n",
- exe.exe(),
- crate::format::io_error(&e),
- dir.display()
- )
- .as_bytes(),
- )
- .await
- .unwrap();
- 1
- }
- };
- async_std::process::ExitStatus::from_raw(code << 8)
+ };
+ async_std::process::ExitStatus::from_raw(code << 8)
+ }
+
+ let exe = exe.clone();
+ let env = env.clone();
+ Ok(Child {
+ fut: Box::pin(async move { async_cd(&exe, &env).await }),
+ wrapped_child: None,
+ })
}
-async fn and(
+fn and(
exe: &crate::parse::Exe,
env: &crate::command::Env,
-) -> async_std::process::ExitStatus {
+) -> anyhow::Result<Child> {
let exe = exe.shift();
if env.latest_status().success() {
- todo!()
- // super::run_exe(&exe, env).await
+ let cmd = crate::command::Command::new(&exe);
+ Ok(Child {
+ fut: Box::pin(async move { unreachable!() }),
+ wrapped_child: Some(Box::new(cmd.spawn(env)?)),
+ })
} else {
- *env.latest_status()
+ let env = env.clone();
+ Ok(Child {
+ fut: Box::pin(async move { *env.latest_status() }),
+ wrapped_child: None,
+ })
}
}
-async fn or(
+fn or(
exe: &crate::parse::Exe,
env: &crate::command::Env,
-) -> async_std::process::ExitStatus {
+) -> anyhow::Result<Child> {
let exe = exe.shift();
if env.latest_status().success() {
- *env.latest_status()
+ let env = env.clone();
+ Ok(Child {
+ fut: Box::pin(async move { *env.latest_status() }),
+ wrapped_child: None,
+ })
} else {
- todo!()
- // super::run_exe(&exe, env).await
+ let cmd = crate::command::Command::new(&exe);
+ Ok(Child {
+ fut: Box::pin(async move { unreachable!() }),
+ wrapped_child: Some(Box::new(cmd.spawn(env)?)),
+ })
}
}
-async fn command(
+fn command(
exe: &crate::parse::Exe,
env: &crate::command::Env,
-) -> async_std::process::ExitStatus {
+) -> anyhow::Result<Child> {
let exe = exe.shift();
- // super::run_binary(&exe, env).await;
- *env.latest_status()
+ let cmd = crate::command::Command::new_binary(&exe);
+ Ok(Child {
+ fut: Box::pin(async move { unreachable!() }),
+ wrapped_child: Some(Box::new(cmd.spawn(env)?)),
+ })
}
-async fn builtin(
+fn builtin(
exe: &crate::parse::Exe,
env: &crate::command::Env,
-) -> async_std::process::ExitStatus {
+) -> anyhow::Result<Child> {
let exe = exe.shift();
- run(&exe, env).unwrap().await;
- *env.latest_status()
+ let cmd = crate::command::Command::new_builtin(&exe);
+ Ok(Child {
+ fut: Box::pin(async move { unreachable!() }),
+ wrapped_child: Some(Box::new(cmd.spawn(env)?)),
+ })
}
fn home() -> std::path::PathBuf {