//! Defines tools for interacting with the environment //! of the shell. use super::{ Error, Result, exec, }; use std::{ io::Write, collections::HashMap, iter::FromIterator }; /// An environment used to [evaluate expressions](mod@super::eval) or /// [execute programs](mod@super::exec). pub trait Env { /// Get the arguments passed to the environment. fn args (&self) -> Args; /// Bind a value to a name. fn bind (&mut self, name: N, value: V) where N: AsRef, V: AsRef; /// Get a bound value from the env. fn get (&self, name: N) -> Option where N: AsRef; /// Get the current working directory. fn working_dir (&self) -> Result; /// Change the working directory. fn set_working_dir

(&mut self, path: P) -> Result<()> where P: AsRef; /// Write to stdout. fn stdout (&mut self, data: R) -> Result<()> where R: ToString; /// Search the `PATH` variable for the `query`. The default implementation /// is derived from [`Env::get`]. fn search (&self, query: S) -> Option where S: AsRef { self.get("PATH")? .split(':') .filter_map(|path| { std::fs::read_dir(path).ok() }) .flat_map(|dir| { dir.filter_map(|entry| entry.ok()) }) .map(|file| file.path()) .find_map(|path| { if path.file_name()? == query.as_ref() { Some (path) } else { None } }) .map(exec::Program::new) } /// Create a scoped environment to isolate variable /// bindings. fn scope (&mut self) -> Scope<'_, Self> where Self: Sized { Scope { bindings: HashMap::new(), parent: self, } } /// Create a new environment that inherits this environments' /// behavior, except the args of this env are overwritten with /// `args`. fn set_args (self, args: A) -> SetArgs where Self: Sized, A: IntoIterator, { SetArgs { parent: self, args: Args::from_iter(args), } } } /// Overwrites the arguments in the wrapped [`Env`]. pub struct SetArgs { parent: E, args: Args, } impl Env for SetArgs { fn working_dir (&self) -> Result { self.parent.working_dir() } fn set_working_dir

(&mut self, path: P) -> Result<()> where P: AsRef { self.parent.set_working_dir(path) } fn search (&self, query: S) -> Option where S: AsRef { self.parent.search(query) } fn stdout (&mut self, data: R) -> Result<()> where R: ToString { self.parent.stdout(data) } fn args (&self) -> Args { self.args.clone() } fn bind (&mut self, name: N, value: V) where N: AsRef, V: AsRef, { self.parent.bind(name, value); } fn get (&self, name: N) -> Option where N: AsRef { self.parent.get(name) } } /// A one-way barrier for variable bindings. Things executed in /// a `Scope` can read the parent's bindings and shadow them, but /// not mutate them. pub struct Scope <'parent, E: Env> { bindings: HashMap , parent: &'parent mut E, } impl Env for Scope<'_, E> { fn working_dir (&self) -> Result { self.parent.working_dir() } fn set_working_dir

(&mut self, path: P) -> Result<()> where P: AsRef { self.parent.set_working_dir(path) } fn search (&self, query: S) -> Option where S: AsRef { self.parent.search(query) } fn stdout (&mut self, data: R) -> Result<()> where R: ToString { self.parent.stdout(data) } fn args (&self) -> Args { self.parent.args() } fn bind (&mut self, name: N, value: V) where N: AsRef, V: AsRef, { self.bindings.insert( name.as_ref().to_string(), value.as_ref().to_string() ); } fn get (&self, name: N) -> Option where N: AsRef { self.bindings .get(name.as_ref()) .cloned() .or_else(|| { self.parent.get(name) }) } } /// An iterator of arguments. #[derive(Default, Clone)] pub struct Args { args: Vec, cur: usize } impl FromIterator for Args { fn from_iter > (iter: T) -> Args { Args { args: iter .into_iter() .map(|s| s.to_string()) .collect(), cur: 0 } } } impl Iterator for Args { type Item = String; fn next (&mut self) -> Option { let Args { args, cur } = self; let val = args.get(*cur)?; *cur += 1; Some (val.clone()) } } /// A completely empty environment. pub struct Pure { bindings: HashMap , cwd: std::path::PathBuf, buf: String, } impl Pure { pub fn init (cwd: impl AsRef) -> Pure { Pure { cwd: cwd.as_ref().to_owned(), bindings: HashMap::new(), buf: String::new(), } } } impl Env for Pure { fn args (&self) -> Args { std::iter::empty::().collect() } fn bind (&mut self, name: N, value: V) where N: AsRef, V: AsRef, { self.bindings.insert( name.as_ref().to_string(), value.as_ref().to_string() ); } fn get (&self, name: N) -> Option where N: AsRef { self.bindings.get(name.as_ref()).cloned() } fn stdout (&mut self, data: R) -> Result<()> where R: ToString { self.buf += &data.to_string(); Ok (()) } fn working_dir (&self) -> Result { if self.cwd.exists() { Ok (self.cwd.clone()) } else { use std::io::ErrorKind; Err (Error::Io (ErrorKind::NotFound.into())) } } fn set_working_dir

(&mut self, path: P) -> Result<()> where P: AsRef { let path = path.as_ref(); if path.is_dir() && path.exists() { Ok (self.cwd = path.to_owned()) } else { use std::io::ErrorKind; Err (Error::Io (ErrorKind::InvalidInput.into())) } } } /// The default global environment, which inherits from the environment of the /// shell process. pub struct Inherit {} impl Env for Inherit { fn args (&self) -> Args { std::env::args().skip(1).collect() } fn bind (&mut self, name: N, value: V) where N: AsRef, V: AsRef, { std::env::set_var(name.as_ref(), value.as_ref()); } fn get (&self, name: N) -> Option where N: AsRef { std::env::var(name.as_ref()).ok() } fn working_dir (&self) -> Result { std::env::current_dir() .map_err(Error::Io) } fn set_working_dir

(&mut self, path: P) -> Result<()> where P: AsRef { let path = path.as_ref(); std::env::set_current_dir(path) .map_err(Error::Io) } fn stdout (&mut self, data: R) -> Result<()> where R: ToString { std::io::stdout().write( data.to_string() .as_bytes() )?; Ok (()) } } /// Initialize the default environment by inheriting from the environment of /// the shell process. pub fn inherit () -> impl Env { Inherit {} } pub mod history; pub mod job; pub mod io;