From 234d71241399ae40d498d7953e9516f5cc5a471c Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 3 Jan 2022 01:19:02 -0500 Subject: start adding back builtin support --- src/builtins.rs | 285 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 145 insertions(+), 140 deletions(-) (limited to 'src/builtins.rs') 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 - + Sync - + Send - + 'a, - >, -> + Sync +type Builtin = &'static (dyn Fn( + &crate::parse::Exe, + &crate::command::Env, +) -> anyhow::Result + + 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: &'static F) -> BuiltinFunc - where - F: for<'a> Fn( - &'a crate::parse::Exe, - &'a crate::command::Env, - ) -> std::pin::Pin< - Box< - dyn std::future::Future - + 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, stdout: Box, stderr: Box, } -impl Builtin { +impl Command { pub fn new(exe: &crate::parse::Exe) -> Option { - 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 { + (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 - + Send - + Sync, + dyn std::future::Future + + 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>, +} + +impl Child { + pub fn id(&self) -> Option { + self.wrapped_child.as_ref().and_then(|cmd| cmd.id()) + } + + #[async_recursion::async_recursion] + pub async fn status( + self, + ) -> anyhow::Result { + 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 { + 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 { 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 { 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 { 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 { 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 { -- cgit v1.2.3-54-g00ecf