- Removed the requirement that Env be object safe and made its API much more user friendly (yay generics)

This commit is contained in:
Riley Apeldoorn 2021-07-06 08:53:00 +02:00
parent ab11cf6f74
commit 3447bcef80
2 changed files with 136 additions and 56 deletions

View File

@ -13,7 +13,10 @@ pub trait Env {
fn args (&self) -> Args; fn args (&self) -> Args;
/// Bind a value to a name. /// Bind a value to a name.
fn bind (&mut self, name: &str, value: &str); fn bind <N, V> (&mut self, name: N, value: V)
where
N: AsRef<str>,
V: AsRef<str>;
/// Get a bound value from the env. /// Get a bound value from the env.
fn get (&self, name: &str) -> Option<String>; fn get (&self, name: &str) -> Option<String>;
@ -22,14 +25,21 @@ pub trait Env {
fn working_dir (&self) -> Result<std::path::PathBuf>; fn working_dir (&self) -> Result<std::path::PathBuf>;
/// Change the working directory. /// Change the working directory.
fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()>; fn set_working_dir <P> (&mut self, path: P) -> Result<()>
where
P: AsRef<std::path::Path>;
/// Write to stdout. /// Write to stdout.
fn stdout (&mut self, data: &str) -> Result<()>; fn stdout <R> (&mut self, data: R) -> Result<()>
where
R: ToString;
/// Search the `PATH` variable for the `query`. The default implementation /// Search the `PATH` variable for the `query`. The default implementation
/// is derived from [`Env::get`]. /// is derived from [`Env::get`].
fn search (&self, query: &str) -> Option<exec::Program> { fn search <S> (&self, query: S) -> Option<exec::Program>
where
S: AsRef<str>
{
self.get("PATH")? self.get("PATH")?
.split(':') .split(':')
.filter_map(|path| { .filter_map(|path| {
@ -40,7 +50,7 @@ pub trait Env {
}) })
.map(|file| file.path()) .map(|file| file.path())
.find_map(|path| { .find_map(|path| {
if path.file_name()? == query { if path.file_name()? == query.as_ref() {
Some (path) Some (path)
} else { } else {
None None
@ -64,13 +74,14 @@ pub trait Env {
/// Create a new environment that inherits this environments' /// Create a new environment that inherits this environments'
/// behavior, except the args of this env are overwritten with /// behavior, except the args of this env are overwritten with
/// `args`. /// `args`.
fn set_args (self, args: Args) -> SetArgs<Self> fn set_args <A> (self, args: A) -> SetArgs<Self>
where where
Self: Sized Self: Sized,
A: IntoIterator<Item = String>,
{ {
SetArgs { SetArgs {
parent: self, parent: self,
args, args: Args::from_iter(args),
} }
} }
@ -88,15 +99,24 @@ impl<E: Env> Env for SetArgs<E> {
self.parent.working_dir() self.parent.working_dir()
} }
fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()> { fn set_working_dir <P> (&mut self, path: P) -> Result<()>
where
P: AsRef<std::path::Path>
{
self.parent.set_working_dir(path) self.parent.set_working_dir(path)
} }
fn search (&self, query: &str) -> Option<exec::Program> { fn search <S> (&self, query: S) -> Option<exec::Program>
where
S: AsRef<str>
{
self.parent.search(query) self.parent.search(query)
} }
fn stdout (&mut self, data: &str) -> Result<()> { fn stdout <R> (&mut self, data: R) -> Result<()>
where
R: ToString
{
self.parent.stdout(data) self.parent.stdout(data)
} }
@ -104,7 +124,11 @@ impl<E: Env> Env for SetArgs<E> {
self.args.clone() self.args.clone()
} }
fn bind (&mut self, name: &str, value: &str) { fn bind <N, V> (&mut self, name: N, value: V)
where
N: AsRef<str>,
V: AsRef<str>,
{
self.parent.bind(name, value); self.parent.bind(name, value);
} }
@ -128,15 +152,24 @@ impl<E: Env> Env for Scope<'_, E> {
self.parent.working_dir() self.parent.working_dir()
} }
fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()> { fn set_working_dir <P> (&mut self, path: P) -> Result<()>
where
P: AsRef<std::path::Path>
{
self.parent.set_working_dir(path) self.parent.set_working_dir(path)
} }
fn search (&self, query: &str) -> Option<exec::Program> { fn search <S> (&self, query: S) -> Option<exec::Program>
where
S: AsRef<str>
{
self.parent.search(query) self.parent.search(query)
} }
fn stdout (&mut self, data: &str) -> Result<()> { fn stdout <R> (&mut self, data: R) -> Result<()>
where
R: ToString
{
self.parent.stdout(data) self.parent.stdout(data)
} }
@ -144,8 +177,15 @@ impl<E: Env> Env for Scope<'_, E> {
self.parent.args() self.parent.args()
} }
fn bind (&mut self, name: &str, value: &str) { fn bind <N, V> (&mut self, name: N, value: V)
self.bindings.insert(name.to_string(), value.to_string()); where
N: AsRef<str>,
V: AsRef<str>,
{
self.bindings.insert(
name.as_ref().to_string(),
value.as_ref().to_string()
);
} }
fn get (&self, name: &str) -> Option<String> { fn get (&self, name: &str) -> Option<String> {
@ -206,16 +246,26 @@ impl Env for Pure {
std::iter::empty::<String>().collect() std::iter::empty::<String>().collect()
} }
fn bind (&mut self, name: &str, value: &str) { fn bind <N, V> (&mut self, name: N, value: V)
self.bindings.insert(name.to_string(), value.to_string()); where
N: AsRef<str>,
V: AsRef<str>,
{
self.bindings.insert(
name.as_ref().to_string(),
value.as_ref().to_string()
);
} }
fn get (&self, name: &str) -> Option<String> { fn get (&self, name: &str) -> Option<String> {
self.bindings.get(name).cloned() self.bindings.get(name).cloned()
} }
fn stdout (&mut self, data: &str) -> Result<()> { fn stdout <R> (&mut self, data: R) -> Result<()>
self.buf += data; where
R: ToString
{
self.buf += &data.to_string();
Ok (()) Ok (())
} }
@ -228,7 +278,12 @@ impl Env for Pure {
} }
} }
fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()> { fn set_working_dir <P> (&mut self, path: P) -> Result<()>
where
P: AsRef<std::path::Path>
{
let path = path.as_ref();
if path.is_dir() && path.exists() { if path.is_dir() && path.exists() {
Ok (self.cwd = path.to_owned()) Ok (self.cwd = path.to_owned())
} else { } else {
@ -247,8 +302,12 @@ impl Env for Inherit {
std::env::args().skip(1).collect() std::env::args().skip(1).collect()
} }
fn bind (&mut self, name: &str, value: &str) { fn bind <N, V> (&mut self, name: N, value: V)
std::env::set_var(name, value); where
N: AsRef<str>,
V: AsRef<str>,
{
std::env::set_var(name.as_ref(), value.as_ref());
} }
fn get (&self, name: &str) -> Option<String> { fn get (&self, name: &str) -> Option<String> {
@ -260,13 +319,24 @@ impl Env for Inherit {
.map_err(Error::Io) .map_err(Error::Io)
} }
fn set_working_dir (&mut self, path: &std::path::Path) -> Result<()> { fn set_working_dir <P> (&mut self, path: P) -> Result<()>
where
P: AsRef<std::path::Path>
{
let path = path.as_ref();
std::env::set_current_dir(path) std::env::set_current_dir(path)
.map_err(Error::Io) .map_err(Error::Io)
} }
fn stdout (&mut self, data: &str) -> Result<()> { fn stdout <R> (&mut self, data: R) -> Result<()>
std::io::stdout().write(data.as_bytes())?; where
R: ToString
{
std::io::stdout().write(
data.to_string().as_bytes()
)?;
Ok (()) Ok (())
} }
} }

View File

@ -15,18 +15,6 @@ pub trait Exec {
} }
impl<F> Exec for F
where
F: Fn (&mut dyn Env) -> Result<()>
{
fn exec <E> (&self, env: &mut E) -> Result<()>
where
E: Env
{
self (env)
}
}
/// A shell command. /// A shell command.
pub enum Command { pub enum Command {
Program (Program), Program (Program),
@ -130,20 +118,30 @@ pub struct NotFound;
/// ``` /// ```
/// ///
/// [`FromStr`]: std::str::FromStr /// [`FromStr`]: std::str::FromStr
pub struct Builtin (fn (&mut dyn Env) -> Result<()>); pub struct Builtin (BuiltinName);
enum BuiltinName {
Disown,
Type,
Exit,
Pwd,
Fg,
Bg,
Cd,
}
impl std::str::FromStr for Builtin { impl std::str::FromStr for Builtin {
type Err = NotFound; type Err = NotFound;
fn from_str (s: &str) -> Result<Self, Self::Err> { fn from_str (s: &str) -> Result<Self, Self::Err> {
let ret = Builtin (match s { let ret = Builtin (match s {
"disown" => builtin::disown, "disown" => BuiltinName::Disown,
"type" => builtin::r#type, "type" => BuiltinName::Type,
"exit" => builtin::exit, "exit" => BuiltinName::Exit,
"pwd" => builtin::pwd, "pwd" => BuiltinName::Pwd,
"fg" => builtin::fg, "fg" => BuiltinName::Fg,
"bg" => builtin::bg, "bg" => BuiltinName::Bg,
"cd" => builtin::cd, "cd" => BuiltinName::Cd,
_ => return Err (NotFound) _ => return Err (NotFound)
}); });
@ -156,7 +154,19 @@ impl Exec for Builtin {
where where
E: Env E: Env
{ {
self.0.exec(env) use BuiltinName::*;
let f = match self.0 {
Disown => builtin::disown,
Type => builtin::r#type,
Exit => builtin::exit,
Pwd => builtin::pwd,
Fg => builtin::fg,
Bg => builtin::bg,
Cd => builtin::cd,
};
f (env)
} }
} }
@ -167,20 +177,20 @@ pub mod builtin {
use super::{ Env, Result, }; use super::{ Env, Result, };
/// Change the current working directory. /// Change the current working directory.
pub fn cd (env: &mut dyn Env) -> Result<()> { pub fn cd (env: &mut impl Env) -> Result<()> {
let arg = env.args().next().unwrap(); let arg = env.args().next().unwrap();
env.set_working_dir(arg.as_ref()) env.set_working_dir(arg)
} }
/// Print the current working directory. /// Print the current working directory.
pub fn pwd (env: &mut dyn Env) -> Result<()> { pub fn pwd (env: &mut impl Env) -> Result<()> {
let cwd = env.working_dir()?; let cwd = env.working_dir()?;
env.stdout(format!("{}", cwd.display()).as_str()); env.stdout(format!("{}", cwd.display()).as_str());
Ok (()) Ok (())
} }
/// Stop the program. Optionally takes an exit code. /// Stop the program. Optionally takes an exit code.
pub fn exit (env: &mut dyn Env) -> Result<()> { pub fn exit (env: &mut impl Env) -> Result<()> {
if let Some (code) = env.args().next().and_then(|s| s.parse::<i32>().ok()) { if let Some (code) = env.args().next().and_then(|s| s.parse::<i32>().ok()) {
std::process::exit(code) std::process::exit(code)
} else { } else {
@ -189,7 +199,7 @@ pub mod builtin {
} }
/// Display information about the type of a command. /// Display information about the type of a command.
pub fn r#type (env: &mut dyn Env) -> Result<()> { pub fn r#type (env: &mut impl Env) -> Result<()> {
use crate::exec::Builtin; use crate::exec::Builtin;
for arg in env.args() { for arg in env.args() {
@ -210,19 +220,19 @@ pub mod builtin {
/// Send a job to the background. See also [`Job::bg`]. /// Send a job to the background. See also [`Job::bg`].
/// ///
/// [`Job::bg`]: crate::job::Job::bg /// [`Job::bg`]: crate::job::Job::bg
pub fn bg (_: &mut dyn Env) -> Result<()> { pub fn bg (_: &mut impl Env) -> Result<()> {
todo!() todo!()
} }
/// Bring a job to the foreground. See also [`Job::fg`]. /// Bring a job to the foreground. See also [`Job::fg`].
/// ///
/// [`Job::fg`]: crate::job::Job::fg /// [`Job::fg`]: crate::job::Job::fg
pub fn fg (_: &mut dyn Env) -> Result<()> { pub fn fg (_: &mut impl Env) -> Result<()> {
todo!() todo!()
} }
/// Disown all background jobs. /// Disown all background jobs.
pub fn disown (_: &mut dyn Env) -> Result<()> { pub fn disown (_: &mut impl Env) -> Result<()> {
todo!() todo!()
} }