- Removed the requirement that Env be object safe and made its API much more user friendly (yay generics)
This commit is contained in:
parent
ab11cf6f74
commit
3447bcef80
2 changed files with 136 additions and 56 deletions
124
src/env.rs
124
src/env.rs
|
@ -13,7 +13,10 @@ pub trait Env {
|
|||
fn args (&self) -> Args;
|
||||
|
||||
/// 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.
|
||||
fn get (&self, name: &str) -> Option<String>;
|
||||
|
@ -22,14 +25,21 @@ pub trait Env {
|
|||
fn working_dir (&self) -> Result<std::path::PathBuf>;
|
||||
|
||||
/// 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.
|
||||
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
|
||||
/// 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")?
|
||||
.split(':')
|
||||
.filter_map(|path| {
|
||||
|
@ -40,7 +50,7 @@ pub trait Env {
|
|||
})
|
||||
.map(|file| file.path())
|
||||
.find_map(|path| {
|
||||
if path.file_name()? == query {
|
||||
if path.file_name()? == query.as_ref() {
|
||||
Some (path)
|
||||
} else {
|
||||
None
|
||||
|
@ -64,13 +74,14 @@ pub trait Env {
|
|||
/// 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<Self>
|
||||
fn set_args <A> (self, args: A) -> SetArgs<Self>
|
||||
where
|
||||
Self: Sized
|
||||
Self: Sized,
|
||||
A: IntoIterator<Item = String>,
|
||||
{
|
||||
SetArgs {
|
||||
parent: self,
|
||||
args,
|
||||
args: Args::from_iter(args),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,15 +99,24 @@ impl<E: Env> Env for SetArgs<E> {
|
|||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn stdout (&mut self, data: &str) -> Result<()> {
|
||||
fn stdout <R> (&mut self, data: R) -> Result<()>
|
||||
where
|
||||
R: ToString
|
||||
{
|
||||
self.parent.stdout(data)
|
||||
}
|
||||
|
||||
|
@ -104,7 +124,11 @@ impl<E: Env> Env for SetArgs<E> {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -128,15 +152,24 @@ impl<E: Env> Env for Scope<'_, E> {
|
|||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn stdout (&mut self, data: &str) -> Result<()> {
|
||||
fn stdout <R> (&mut self, data: R) -> Result<()>
|
||||
where
|
||||
R: ToString
|
||||
{
|
||||
self.parent.stdout(data)
|
||||
}
|
||||
|
||||
|
@ -144,8 +177,15 @@ impl<E: Env> Env for Scope<'_, E> {
|
|||
self.parent.args()
|
||||
}
|
||||
|
||||
fn bind (&mut self, name: &str, value: &str) {
|
||||
self.bindings.insert(name.to_string(), value.to_string());
|
||||
fn bind <N, V> (&mut self, name: N, value: V)
|
||||
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> {
|
||||
|
@ -206,16 +246,26 @@ impl Env for Pure {
|
|||
std::iter::empty::<String>().collect()
|
||||
}
|
||||
|
||||
fn bind (&mut self, name: &str, value: &str) {
|
||||
self.bindings.insert(name.to_string(), value.to_string());
|
||||
fn bind <N, V> (&mut self, name: N, value: V)
|
||||
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> {
|
||||
self.bindings.get(name).cloned()
|
||||
}
|
||||
|
||||
fn stdout (&mut self, data: &str) -> Result<()> {
|
||||
self.buf += data;
|
||||
fn stdout <R> (&mut self, data: R) -> Result<()>
|
||||
where
|
||||
R: ToString
|
||||
{
|
||||
self.buf += &data.to_string();
|
||||
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() {
|
||||
Ok (self.cwd = path.to_owned())
|
||||
} else {
|
||||
|
@ -247,8 +302,12 @@ impl Env for Inherit {
|
|||
std::env::args().skip(1).collect()
|
||||
}
|
||||
|
||||
fn bind (&mut self, name: &str, value: &str) {
|
||||
std::env::set_var(name, value);
|
||||
fn bind <N, V> (&mut self, name: N, value: V)
|
||||
where
|
||||
N: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
std::env::set_var(name.as_ref(), value.as_ref());
|
||||
}
|
||||
|
||||
fn get (&self, name: &str) -> Option<String> {
|
||||
|
@ -260,13 +319,24 @@ impl Env for Inherit {
|
|||
.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)
|
||||
.map_err(Error::Io)
|
||||
}
|
||||
|
||||
fn stdout (&mut self, data: &str) -> Result<()> {
|
||||
std::io::stdout().write(data.as_bytes())?;
|
||||
fn stdout <R> (&mut self, data: R) -> Result<()>
|
||||
where
|
||||
R: ToString
|
||||
{
|
||||
std::io::stdout().write(
|
||||
data.to_string().as_bytes()
|
||||
)?;
|
||||
|
||||
Ok (())
|
||||
}
|
||||
}
|
||||
|
|
68
src/exec.rs
68
src/exec.rs
|
@ -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.
|
||||
pub enum Command {
|
||||
Program (Program),
|
||||
|
@ -130,20 +118,30 @@ pub struct NotFound;
|
|||
/// ```
|
||||
///
|
||||
/// [`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 {
|
||||
type Err = NotFound;
|
||||
|
||||
fn from_str (s: &str) -> Result<Self, Self::Err> {
|
||||
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,
|
||||
"disown" => BuiltinName::Disown,
|
||||
"type" => BuiltinName::Type,
|
||||
"exit" => BuiltinName::Exit,
|
||||
"pwd" => BuiltinName::Pwd,
|
||||
"fg" => BuiltinName::Fg,
|
||||
"bg" => BuiltinName::Bg,
|
||||
"cd" => BuiltinName::Cd,
|
||||
_ => return Err (NotFound)
|
||||
});
|
||||
|
||||
|
@ -156,7 +154,19 @@ impl Exec for Builtin {
|
|||
where
|
||||
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, };
|
||||
|
||||
/// 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();
|
||||
env.set_working_dir(arg.as_ref())
|
||||
env.set_working_dir(arg)
|
||||
}
|
||||
|
||||
/// 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()?;
|
||||
env.stdout(format!("{}", cwd.display()).as_str());
|
||||
Ok (())
|
||||
}
|
||||
|
||||
/// 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()) {
|
||||
std::process::exit(code)
|
||||
} else {
|
||||
|
@ -189,7 +199,7 @@ pub mod builtin {
|
|||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
for arg in env.args() {
|
||||
|
@ -210,19 +220,19 @@ pub mod builtin {
|
|||
/// Send a job to the background. See also [`Job::bg`].
|
||||
///
|
||||
/// [`Job::bg`]: crate::job::Job::bg
|
||||
pub fn bg (_: &mut dyn Env) -> Result<()> {
|
||||
pub fn bg (_: &mut impl 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<()> {
|
||||
pub fn fg (_: &mut impl Env) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Disown all background jobs.
|
||||
pub fn disown (_: &mut dyn Env) -> Result<()> {
|
||||
pub fn disown (_: &mut impl Env) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue