aboutsummaryrefslogtreecommitdiffstats
path: root/src/builtins.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/builtins.rs')
-rw-r--r--src/builtins.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/builtins.rs b/src/builtins.rs
new file mode 100644
index 0000000..f4c8c44
--- /dev/null
+++ b/src/builtins.rs
@@ -0,0 +1,71 @@
+use snafu::{ResultExt, Snafu};
+
+#[derive(Debug, Snafu)]
+pub enum Error {
+ #[snafu(display("unknown builtin {}", cmd))]
+ UnknownBuiltin { cmd: String },
+
+ #[snafu(display("failed to cd to {}: {}", dir, source))]
+ Chdir { dir: String, source: nix::Error },
+
+ #[snafu(display("cd requires a directory to be given"))]
+ ChdirParams,
+}
+
+pub fn exec(cmd: &str, args: &[String]) -> Result<Builtin, Error> {
+ Builtin::new(cmd, args)
+}
+
+pub struct Builtin {
+ cmd: String,
+ args: Vec<String>,
+ done: bool,
+}
+
+impl Builtin {
+ fn new(cmd: &str, args: &[String]) -> Result<Self, Error> {
+ match cmd {
+ "cd" => Ok(Builtin {
+ cmd: cmd.to_string(),
+ args: args.to_vec(),
+ done: false,
+ }),
+ _ => Err(Error::UnknownBuiltin {
+ cmd: cmd.to_string(),
+ }),
+ }
+ }
+}
+
+#[must_use = "streams do nothing unless polled"]
+impl futures::stream::Stream for Builtin {
+ type Item = crate::eval::CommandEvent;
+ type Error = Error;
+
+ fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
+ if self.done {
+ return Ok(futures::Async::Ready(None));
+ }
+
+ self.done = true;
+ let res = match self.cmd.as_ref() {
+ "cd" => cd(&self.args),
+ _ => Err(Error::UnknownBuiltin {
+ cmd: self.cmd.clone(),
+ }),
+ };
+ res.map(|_| {
+ futures::Async::Ready(Some(
+ crate::eval::CommandEvent::BuiltinExit,
+ ))
+ })
+ }
+}
+
+fn cd(args: &[String]) -> Result<(), Error> {
+ if let Some(dir) = args.get(0) {
+ nix::unistd::chdir(dir.as_str()).context(Chdir { dir: dir.clone() })
+ } else {
+ Err(Error::ChdirParams)
+ }
+}