From ab11cf6f742b4251d92f2ac430cde2fbac9a3672 Mon Sep 17 00:00:00 2001 From: Riley Apeldoorn Date: Mon, 5 Jul 2021 22:17:49 +0200 Subject: [PATCH] Restructuring + added the SetArgs Env --- src/env.rs | 282 ++++++++++++++++++++++++ src/env/history.rs | 1 + src/env/io.rs | 1 + src/env/job.rs | 39 ++++ src/eval.rs | 28 +++ src/exec.rs | 231 +++++++++++++++++++ src/lib.rs | 539 +-------------------------------------------- 7 files changed, 586 insertions(+), 535 deletions(-) create mode 100644 src/env.rs create mode 100644 src/env/history.rs create mode 100644 src/env/io.rs create mode 100644 src/env/job.rs create mode 100644 src/eval.rs create mode 100644 src/exec.rs diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000..94dfabf --- /dev/null +++ b/src/env.rs @@ -0,0 +1,282 @@ +//! Defines tools for interacting with the environment +//! of the shell. + +use super::{ Error, Result, exec, }; + +use std::{collections::HashMap, io::Write, 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: &str, value: &str); + + /// Get a bound value from the env. + fn get (&self, name: &str) -> Option; + + /// Get the current working directory. + fn working_dir (&self) -> Result; + + /// Change the working directory. + fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()>; + + /// Write to stdout. + fn stdout (&mut self, data: &str) -> Result<()>; + + /// Search the `PATH` variable for the `query`. The default implementation + /// is derived from [`Env::get`]. + fn search (&self, query: &str) -> Option { + 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 { + 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: Args) -> SetArgs + where + Self: Sized + { + SetArgs { + parent: self, + 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: &std::path::Path) -> Result<()> { + self.parent.set_working_dir(path) + } + + fn search (&self, query: &str) -> Option { + self.parent.search(query) + } + + fn stdout (&mut self, data: &str) -> Result<()> { + self.parent.stdout(data) + } + + fn args (&self) -> Args { + self.args.clone() + } + + fn bind (&mut self, name: &str, value: &str) { + self.parent.bind(name, value); + } + + fn get (&self, name: &str) -> Option { + 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: &std::path::Path) -> Result<()> { + self.parent.set_working_dir(path) + } + + fn search (&self, query: &str) -> Option { + self.parent.search(query) + } + + fn stdout (&mut self, data: &str) -> Result<()> { + self.parent.stdout(data) + } + + fn args (&self) -> Args { + self.parent.args() + } + + fn bind (&mut self, name: &str, value: &str) { + self.bindings.insert(name.to_string(), value.to_string()); + } + + fn get (&self, name: &str) -> Option { + self.bindings + .get(name) + .cloned() + .or_else(|| { + self.parent.get(name) + }) + } +} + +/// An iterator of arguments. +#[derive(Default, Clone)] +pub struct Args (Vec); + +impl FromIterator for Args { + fn from_iter > (iter: T) -> Args { + Args ( + iter.into_iter() + .map(|s| s.to_string()) + .collect() + ) + } +} + +impl Iterator for Args { + type Item = String; + + fn next (&mut self) -> Option { + if self.0.len() > 0 { + Some (self.0.remove(0)) + } else { + None + } + } +} + +/// 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: &str, value: &str) { + self.bindings.insert(name.to_string(), value.to_string()); + } + + fn get (&self, name: &str) -> Option { + self.bindings.get(name).cloned() + } + + fn stdout (&mut self, data: &str) -> Result<()> { + self.buf += data; + 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: &std::path::Path) -> Result<()> { + 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: &str, value: &str) { + std::env::set_var(name, value); + } + + fn get (&self, name: &str) -> Option { + std::env::var(name).ok() + } + + fn working_dir (&self) -> Result { + std::env::current_dir() + .map_err(Error::Io) + } + + fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()> { + std::env::set_current_dir(path) + .map_err(Error::Io) + } + + fn stdout (&mut self, data: &str) -> Result<()> { + std::io::stdout().write(data.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; diff --git a/src/env/history.rs b/src/env/history.rs new file mode 100644 index 0000000..487fb4c --- /dev/null +++ b/src/env/history.rs @@ -0,0 +1 @@ +//! History control. diff --git a/src/env/io.rs b/src/env/io.rs new file mode 100644 index 0000000..72cc622 --- /dev/null +++ b/src/env/io.rs @@ -0,0 +1 @@ +//! Interaction with stdio and the file system. diff --git a/src/env/job.rs b/src/env/job.rs new file mode 100644 index 0000000..7a9ff72 --- /dev/null +++ b/src/env/job.rs @@ -0,0 +1,39 @@ +//! Job scheduling. + +use super::Result; + +use std::marker::PhantomData; + +use sealed::Sealed; +mod sealed { + pub trait Sealed {} +} + +/// An empty type used to tag a [`Job`] as running in the background. +pub enum Stopped {} +impl Sealed for Stopped {} + +/// An empty type used to tag a [`Job`] as running in the foreground. +pub enum Running {} +impl Sealed for Running {} + +/// Represents a running process, either in the foreground +/// or in the background. +pub struct Job { + _t: PhantomData, +} + +impl Job { + + /// Attempt to send this job to the background. + pub fn bg (self) -> Result> { todo!() } + +} + +impl Job { + + /// Attempt to pull this job to the foreground. + pub fn fg (self) -> Result> { todo!() } + +} + diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..7f89022 --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,28 @@ +//! The Rush scripting language. + +use super::{ + Result, + env::Env, +}; + +/// The value of an expression. +pub enum Value { + String (String) +} + +/// An identifier. +pub struct Name (String); + +/// Evaluation of expressions in the Rush scripting language. +pub trait Eval { + + /// Evaluates the code in the context of the given [`Env`]. + fn eval (self, env: &E) -> Result + where + E: Env; + +} + +/// An iterator of parsed code generated from an iterator of raw +/// code fragments. +pub struct Parser; diff --git a/src/exec.rs b/src/exec.rs new file mode 100644 index 0000000..9c2a21d --- /dev/null +++ b/src/exec.rs @@ -0,0 +1,231 @@ +//! Execution of commands, builtins and code. + +use super::{ + Result, + env::Env, +}; + +/// Execute the code in the given environment. +pub trait Exec { + + /// Run the executable. + fn exec (&self, env: &mut E) -> Result<()> + where + E: Env; + +} + +impl Exec for F +where + F: Fn (&mut dyn Env) -> Result<()> +{ + fn exec (&self, env: &mut E) -> Result<()> + where + E: Env + { + self (env) + } +} + +/// A shell command. +pub enum Command { + Program (Program), + Builtin (Builtin), +} + +impl Exec for Command { + fn exec(&self, env: &mut E) -> Result<()> + where + E: Env + { + match self { + Command::Builtin (builtin) => builtin.exec(env), + Command::Program (program) => program.exec(env), + } + } +} + +/// A path to an executable file representing an external program. +pub struct Program (std::ffi::OsString); + +impl Program { + + /// Create a new [`Program`] struct. + pub (super) fn new (str: impl AsRef) -> Program { + Program (str.as_ref().to_owned()) + } + + /// Get the command name as an [`OsStr`](std::ffi::OsStr) + pub fn as_os_str (&self) -> &std::ffi::OsStr { + &self.0 + } + + /// Get the [`Path`](std::path::Path) of the file the name references. + pub fn as_path (&self) -> &std::path::Path { + self.as_ref() + } +} + +impl AsRef for Program { + fn as_ref (&self) -> &std::path::Path { + std::path::Path::new(&self.0) + } +} + +impl Exec for Program { + fn exec (&self, env: &mut E) -> Result<()> + where + E: Env + { + use ptyprocess::PtyProcess; + use std::io::{ BufRead, Read }; + + let mut cmd = std::process::Command::new(self.as_os_str()); + + cmd.current_dir(env.working_dir()?); + cmd.args(env.args()); + + let proc = PtyProcess::spawn(cmd) + .unwrap(); + + let mut pty = proc + .get_pty_handle() + .unwrap(); + + while let Ok (true) = proc.is_alive() { + let mut buf = String::new(); + pty.read_to_string(&mut buf); + env.stdout(&buf); + } + + Ok (()) + } +} + + +/// Represents the case that a string fails to parse to a [`Builtin`] +/// because the associated builtin does not exist. Valid builtin names +/// are listed in the [`builtin`] module. +#[derive(Debug)] +pub struct NotFound; + +/// A shell builtin. See also the [`builtin`] module. +/// +/// # Examples +/// +/// The only way to get a `Builtin` is through [`FromStr`]. +/// +/// ``` +/// # use rush::exec::Builtin; +/// "type".parse::().unwrap(); +/// ``` +/// +/// Parsing an unknown builtin name yields an error. +/// +/// ```should_panic +/// # use rush::exec::Builtin; +/// "sjdkgfhgfkfg" +/// .parse::() +/// .unwrap(); +/// ``` +/// +/// [`FromStr`]: std::str::FromStr +pub struct Builtin (fn (&mut dyn Env) -> Result<()>); + +impl std::str::FromStr for Builtin { + type Err = NotFound; + + fn from_str (s: &str) -> Result { + let ret = Builtin (match s { + "disown" => builtin::disown, + "type" => builtin::r#type, + "exit" => builtin::exit, + "pwd" => builtin::pwd, + "fg" => builtin::fg, + "bg" => builtin::bg, + "cd" => builtin::cd, + _ => return Err (NotFound) + }); + + Ok (ret) + } +} + +impl Exec for Builtin { + fn exec (&self, env: &mut E) -> Result<()> + where + E: Env + { + self.0.exec(env) + } +} + +pub mod builtin { + + //! Shell builtins. + + use super::{ Env, Result, }; + + /// Change the current working directory. + pub fn cd (env: &mut dyn Env) -> Result<()> { + let arg = env.args().next().unwrap(); + env.set_working_dir(arg.as_ref()) + } + + /// Print the current working directory. + pub fn pwd (env: &mut dyn Env) -> Result<()> { + let cwd = env.working_dir()?; + env.stdout(format!("{}", cwd.display()).as_str()); + Ok (()) + } + + /// Stop the program. Optionally takes an exit code. + pub fn exit (env: &mut dyn Env) -> Result<()> { + if let Some (code) = env.args().next().and_then(|s| s.parse::().ok()) { + std::process::exit(code) + } else { + std::process::exit(0) + } + } + + /// Display information about the type of a command. + pub fn r#type (env: &mut dyn Env) -> Result<()> { + use crate::exec::Builtin; + + for arg in env.args() { + let output = if let Ok (_) = arg.parse::() { + format!("{} is a shell builtin", arg) + } else if let Some (path) = env.search(&arg) { + format!("{} is {}", arg, path.as_path().display()) + } else { + format!("type: {}: not found", arg) + }; + + env.stdout((output + "\n").as_str())?; + } + + Ok (()) + } + + /// Send a job to the background. See also [`Job::bg`]. + /// + /// [`Job::bg`]: crate::job::Job::bg + pub fn bg (_: &mut dyn Env) -> Result<()> { + todo!() + } + + /// Bring a job to the foreground. See also [`Job::fg`]. + /// + /// [`Job::fg`]: crate::job::Job::fg + pub fn fg (_: &mut dyn Env) -> Result<()> { + todo!() + } + + /// Disown all background jobs. + pub fn disown (_: &mut dyn Env) -> Result<()> { + todo!() + } + +} + + diff --git a/src/lib.rs b/src/lib.rs index 9f115cd..e9ca699 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,9 @@ //! The Rush shell. +pub mod env; +pub mod exec; +pub mod eval; + use env::job; /// Attempt to execute a program, rush expression or builtin. @@ -7,541 +11,6 @@ pub fn exec (prog: impl exec::Exec) -> Result> { todo!() } -pub mod exec { - - //! Execution of commands, builtins and code. - - use super::{ - Result, - env::Env, - }; - - /// Execute the code in the given environment. - pub trait Exec { - - /// Run the executable. - fn exec (&self, env: &mut E) -> Result<()> - where - E: Env; - - } - - impl Exec for F - where - F: Fn (&mut dyn Env) -> Result<()> - { - fn exec (&self, env: &mut E) -> Result<()> - where - E: Env - { - self (env) - } - } - - /// A shell command. - pub enum Command { - Program (Program), - Builtin (Builtin), - } - - impl Exec for Command { - fn exec(&self, env: &mut E) -> Result<()> - where - E: Env - { - match self { - Command::Builtin (builtin) => builtin.exec(env), - Command::Program (program) => program.exec(env), - } - } - } - - /// A path to an executable file representing an external program. - pub struct Program (std::ffi::OsString); - - impl Program { - - /// Create a new [`Program`] struct. - pub (super) fn new (str: impl AsRef) -> Program { - Program (str.as_ref().to_owned()) - } - - /// Get the command name as an [`OsStr`](std::ffi::OsStr) - pub fn as_os_str (&self) -> &std::ffi::OsStr { - &self.0 - } - - /// Get the [`Path`](std::path::Path) of the file the name references. - pub fn as_path (&self) -> &std::path::Path { - self.as_ref() - } - } - - impl AsRef for Program { - fn as_ref (&self) -> &std::path::Path { - std::path::Path::new(&self.0) - } - } - - impl Exec for Program { - fn exec (&self, env: &mut E) -> Result<()> - where - E: Env - { - use ptyprocess::PtyProcess; - use std::io::{ BufRead, Read }; - - let mut cmd = std::process::Command::new(self.as_os_str()); - - cmd.current_dir(env.working_dir()?); - cmd.args(env.args()); - - let proc = PtyProcess::spawn(cmd) - .unwrap(); - - let mut pty = proc - .get_pty_handle() - .unwrap(); - - while let Ok (true) = proc.is_alive() { - let mut buf = String::new(); - pty.read_to_string(&mut buf); - println!("{}", buf); - } - - Ok (()) - } - } - - - /// Represents the case that a string fails to parse to a [`Builtin`] - /// because the associated builtin does not exist. Valid builtin names - /// are listed in the [`builtin`] module. - #[derive(Debug)] - pub struct NotFound; - - /// A shell builtin. See also the [`builtin`] module. - /// - /// # Examples - /// - /// The only way to get a `Builtin` is through [`FromStr`]. - /// - /// ``` - /// # use rush::exec::Builtin; - /// "type".parse::().unwrap(); - /// ``` - /// - /// Parsing an unknown builtin name yields an error. - /// - /// ```should_panic - /// # use rush::exec::Builtin; - /// "sjdkgfhgfkfg" - /// .parse::() - /// .unwrap(); - /// ``` - /// - /// [`FromStr`]: std::str::FromStr - pub struct Builtin (fn (&mut dyn Env) -> Result<()>); - - impl std::str::FromStr for Builtin { - type Err = NotFound; - - fn from_str (s: &str) -> Result { - let ret = Builtin (match s { - "disown" => builtin::disown, - "type" => builtin::r#type, - "exit" => builtin::exit, - "pwd" => builtin::pwd, - "fg" => builtin::fg, - "bg" => builtin::bg, - "cd" => builtin::cd, - _ => return Err (NotFound) - }); - - Ok (ret) - } - } - - impl Exec for Builtin { - fn exec (&self, env: &mut E) -> Result<()> - where - E: Env - { - self.0.exec(env) - } - } - - pub mod builtin { - - //! Shell builtins. - - use super::{ Env, Result, }; - - /// Change the current working directory. - pub fn cd (env: &mut dyn Env) -> Result<()> { - let arg = env.args().next().unwrap(); - env.set_working_dir(arg.as_ref()) - } - - /// Print the current working directory. - pub fn pwd (env: &mut dyn Env) -> Result<()> { - let cwd = env.working_dir()?; - println!("{}", cwd.display()); - Ok (()) - } - - /// Stop the program. Optionally takes an exit code. - pub fn exit (env: &mut dyn Env) -> Result<()> { - if let Some (code) = env.args().next().and_then(|s| s.parse::().ok()) { - std::process::exit(code) - } else { - std::process::exit(0) - } - } - - /// Display information about the type of a command. - pub fn r#type (env: &mut dyn Env) -> Result<()> { - use crate::exec::Builtin; - - for arg in env.args() { - if let Ok (_) = arg.parse::() { - println!("{} is a shell builtin", arg); - } else if let Some (path) = env.search(&arg) { - println!("{} is {}", arg, path.as_path().display()); - } else { - println!("type: {}: not found", arg); - } - } - - Ok (()) - } - - /// Send a job to the background. See also [`Job::bg`]. - /// - /// [`Job::bg`]: crate::job::Job::bg - pub fn bg (_: &mut dyn Env) -> Result<()> { - todo!() - } - - /// Bring a job to the foreground. See also [`Job::fg`]. - /// - /// [`Job::fg`]: crate::job::Job::fg - pub fn fg (_: &mut dyn Env) -> Result<()> { - todo!() - } - - /// Disown all background jobs. - pub fn disown (_: &mut dyn Env) -> Result<()> { - todo!() - } - - } - - -} - -pub mod eval { - - //! The Rush scripting language. - - use super::{ - Result, - env::Env, - }; - - /// The value of an expression. - pub enum Value { - String (String) - } - - /// An identifier. - pub struct Name (String); - - /// Evaluation of expressions in the Rush scripting language. - pub trait Eval { - - /// Evaluates the code in the context of the given [`Env`]. - fn eval (self, env: &E) -> Result - where - E: Env; - - } - - /// An iterator of parsed code generated from an iterator of raw - /// code fragments. - pub struct Parser; -} - -pub mod env { - - //! Defines tools for interacting with the environment - //! of the shell. - - use super::{ Error, Result, exec, }; - - use std::{ - iter::FromIterator, - collections::HashMap, - }; - - /// 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: &str, value: &str); - - /// Get a bound value from the env. - fn get (&self, name: &str) -> Option; - - /// Get the current working directory. - fn working_dir (&self) -> Result; - - /// Change the working directory. - fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()>; - - /// Search the `PATH` variable for the `query`. The default implementation - /// is derived from [`Env::get`]. - fn search (&self, query: &str) -> Option { - 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 { - 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, - } - } - - } - - /// 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: &std::path::Path) -> Result<()> { - self.parent.set_working_dir(path) - } - - fn search (&self, query: &str) -> Option { - self.parent.search(query) - } - - fn args (&self) -> Args { - self.parent.args() - } - - fn bind (&mut self, name: &str, value: &str) { - self.bindings.insert(name.to_string(), value.to_string()); - } - - fn get (&self, name: &str) -> Option { - self.bindings - .get(name) - .cloned() - .or_else(|| { - self.parent.get(name) - }) - } - } - - /// An iterator of arguments. - #[derive(Default)] - pub struct Args (Vec); - - impl FromIterator for Args { - fn from_iter > (iter: T) -> Args { - Args ( - iter.into_iter() - .map(|s| s.to_string()) - .collect() - ) - } - } - - impl Iterator for Args { - type Item = String; - - fn next (&mut self) -> Option { - if self.0.len() > 0 { - Some (self.0.remove(0)) - } else { - None - } - } - } - - /// A completely empty environment. - pub struct Pure { - bindings: HashMap , - cwd: std::path::PathBuf, - } - - impl Pure { - pub fn init (cwd: impl AsRef) -> Pure { - Pure { - cwd: cwd.as_ref().to_owned(), - bindings: HashMap::new(), - } - } - } - - impl Env for Pure { - fn args (&self) -> Args { - std::iter::empty::().collect() - } - - fn bind (&mut self, name: &str, value: &str) { - self.bindings.insert(name.to_string(), value.to_string()); - } - - fn get (&self, name: &str) -> Option { - self.bindings.get(name).cloned() - } - - 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: &std::path::Path) -> Result<()> { - 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: &str, value: &str) { - std::env::set_var(name, value); - } - - fn get (&self, name: &str) -> Option { - std::env::var(name).ok() - } - - fn working_dir (&self) -> Result { - std::env::current_dir() - .map_err(Error::Io) - } - - fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()> { - std::env::set_current_dir(path) - .map_err(Error::Io) - } - } - - /// Initialize the default environment by inheriting from the environment of - /// the shell process. - pub fn inherit () -> impl Env { - Inherit {} - } - - pub mod history { - - //! History control. - - } - - pub mod io { - - //! Interaction with stdio and the file system. - - } - - pub mod job { - - //! Job scheduling. - - use super::Result; - - use std::marker::PhantomData; - - use sealed::Sealed; - mod sealed { - pub trait Sealed {} - } - - /// An empty type used to tag a [`Job`] as running in the background. - pub enum Stopped {} - impl Sealed for Stopped {} - - /// An empty type used to tag a [`Job`] as running in the foreground. - pub enum Running {} - impl Sealed for Running {} - - /// Represents a running process, either in the foreground - /// or in the background. - pub struct Job { - _t: PhantomData, - } - - impl Job { - - /// Attempt to send this job to the background. - pub fn bg (self) -> Result> { todo!() } - - } - - impl Job { - - /// Attempt to pull this job to the foreground. - pub fn fg (self) -> Result> { todo!() } - - } - - } -} - pub use err::{ Error, Result,