Browse Source

feat: add command to change prompt style

main
fdai7451 2 years ago
parent
commit
06ce1ffe25
  1. 17
      src/builtins/cd.rs
  2. 30
      src/builtins/change_prompt.rs
  3. 4
      src/builtins/exit.rs
  4. 34
      src/builtins/mod.rs
  5. 10
      src/execute.rs
  6. 6
      src/main.rs
  7. 1
      src/prompt.rs

17
src/builtins/cd.rs

@ -1,4 +1,4 @@
use crate::builtins::Builtin;
use crate::builtins::{Builtin, BuiltinConfig};
use std::env::set_current_dir; use std::env::set_current_dir;
use crate::error::ShellError; use crate::error::ShellError;
@ -6,7 +6,7 @@ use crate::error::ShellError;
pub struct Cd; pub struct Cd;
impl Builtin for Cd { impl Builtin for Cd {
fn execute(&mut self, args: Vec<String>) -> Result<(), ShellError> {
fn execute(&mut self, _: &mut BuiltinConfig, args: Vec<String>) -> Result<(), ShellError> {
let Some(first) = args.get(0) else { let Some(first) = args.get(0) else {
return Err(ShellError::ExecuteFailure("no argument given".to_string())) return Err(ShellError::ExecuteFailure("no argument given".to_string()))
}; };
@ -22,7 +22,10 @@ mod tests {
#[test] #[test]
fn test_cd_current() { fn test_cd_current() {
assert_eq!(Cd.execute(vec![".".to_string()]), Ok(()))
assert_eq!(
Cd.execute(&mut BuiltinConfig::new(), vec![".".to_string()]),
Ok(())
)
} }
#[test] #[test]
@ -37,6 +40,12 @@ mod tests {
} }
// No such file or directory (os error 2) // No such file or directory (os error 2)
assert_ne!(Cd.execute(vec![path.to_string_lossy().to_string()]), Ok(()))
assert_ne!(
Cd.execute(
&mut BuiltinConfig::new(),
vec![path.to_string_lossy().to_string()]
),
Ok(())
)
} }
} }

30
src/builtins/change_prompt.rs

@ -0,0 +1,30 @@
use crate::builtins::{Builtin, BuiltinConfig};
use crate::error::ShellError;
use crate::prompt::PromptStyle;
pub struct ChangePrompt;
impl Builtin for ChangePrompt {
fn execute(&mut self, config: &mut BuiltinConfig, args: Vec<String>) -> Result<(), ShellError> {
if args.len() > 1 {
Err(ShellError::ExecuteFailure(
"expected only one argument".to_string(),
))
} else if let Some(style) = args.get(0) {
match style.to_lowercase().as_str() {
"none" => config.prompt_style = PromptStyle::None,
_ => {
return Err(ShellError::ExecuteFailure(format!(
"{} is not a valid prompt style",
style
)))
}
}
Ok(())
} else {
Err(ShellError::ExecuteFailure(
"expected at least one argument".to_string(),
))
}
}
}

4
src/builtins/exit.rs

@ -1,10 +1,10 @@
use crate::builtins::Builtin;
use crate::builtins::{Builtin, BuiltinConfig};
use crate::error::ShellError; use crate::error::ShellError;
pub struct Exit; pub struct Exit;
impl Builtin for Exit { impl Builtin for Exit {
fn execute(&mut self, _: Vec<String>) -> Result<(), ShellError> {
fn execute(&mut self, _: &mut BuiltinConfig, _: Vec<String>) -> Result<(), ShellError> {
std::process::exit(0) std::process::exit(0)
} }
} }

34
src/builtins/mod.rs

@ -1,26 +1,50 @@
use crate::error::ShellError; use crate::error::ShellError;
use crate::prompt::PromptStyle;
use once_cell::unsync::Lazy; use once_cell::unsync::Lazy;
mod cd; mod cd;
mod change_prompt;
mod exit; mod exit;
pub struct BuiltinConfig {
pub prompt_style: PromptStyle,
}
impl BuiltinConfig {
pub fn new() -> Self {
Self {
prompt_style: PromptStyle::None,
}
}
}
trait Builtin: Sync + Send { trait Builtin: Sync + Send {
fn execute(&mut self, args: Vec<String>) -> Result<(), ShellError>;
fn execute(&mut self, config: &mut BuiltinConfig, args: Vec<String>) -> Result<(), ShellError>;
} }
const BUILTINS: Lazy<Vec<(&str, Box<dyn Builtin>)>> =
Lazy::new(|| vec![("cd", Box::new(cd::Cd)), ("exit", Box::new(exit::Exit))]);
const BUILTINS: Lazy<Vec<(&str, Box<dyn Builtin>)>> = Lazy::new(|| {
vec![
("cd", Box::new(cd::Cd)),
("change-prompt", Box::new(change_prompt::ChangePrompt)),
("exit", Box::new(exit::Exit)),
]
});
pub fn is_builtin(keyword: &str) -> bool { pub fn is_builtin(keyword: &str) -> bool {
BUILTINS.iter().find(|(k, _)| k == &keyword).is_some() BUILTINS.iter().find(|(k, _)| k == &keyword).is_some()
} }
#[allow(const_item_mutation)] #[allow(const_item_mutation)]
pub fn execute_builtin(keyword: &str, args: Vec<String>) -> Result<(), ShellError> {
pub fn execute_builtin(
keyword: &str,
config: &mut BuiltinConfig,
args: Vec<String>,
) -> Result<(), ShellError> {
if let Some(builtin) = BUILTINS if let Some(builtin) = BUILTINS
.iter_mut() .iter_mut()
.find_map(|(k, c)| if k == &keyword { Some(c) } else { None }) .find_map(|(k, c)| if k == &keyword { Some(c) } else { None })
{ {
builtin.execute(args)?
builtin.execute(config, args)?
} }
Ok(()) Ok(())
} }

10
src/execute.rs

@ -3,18 +3,22 @@ use std::io::ErrorKind;
use std::process::{Command, ExitStatus}; use std::process::{Command, ExitStatus};
use std::{io, thread}; use std::{io, thread};
use crate::builtins::{execute_builtin, is_builtin};
use crate::builtins::{execute_builtin, is_builtin, BuiltinConfig};
use crate::error::ShellError; use crate::error::ShellError;
use crate::parse::parse_line; use crate::parse::parse_line;
pub fn interpret(line: String, ctrlc_recv: Receiver<()>) -> Result<(), ShellError> {
pub fn interpret(
line: String,
config: &mut BuiltinConfig,
ctrlc_recv: Receiver<()>,
) -> Result<(), ShellError> {
if line.is_empty() { if line.is_empty() {
return Err(ShellError::EmptyLine); return Err(ShellError::EmptyLine);
} }
let (keyword, args) = parse_line(&line)?; let (keyword, args) = parse_line(&line)?;
if is_builtin(keyword) { if is_builtin(keyword) {
execute_builtin(keyword, args)?;
execute_builtin(keyword, config, args)?;
} else { } else {
let mut command = Command::new(keyword); let mut command = Command::new(keyword);
command.args(args); command.args(args);

6
src/main.rs

@ -4,6 +4,7 @@ mod execute;
mod parse; mod parse;
mod prompt; mod prompt;
use crate::builtins::BuiltinConfig;
use crate::error::ShellError; use crate::error::ShellError;
use execute::interpret; use execute::interpret;
use rustyline::config::Configurer; use rustyline::config::Configurer;
@ -22,13 +23,14 @@ fn main() -> Result<()> {
rl.set_auto_add_history(true); rl.set_auto_add_history(true);
let mut prompt = Prompt::new(); let mut prompt = Prompt::new();
let mut config = BuiltinConfig::new();
loop { loop {
let readline = rl.readline(&prompt.get_prompt()); let readline = rl.readline(&prompt.get_prompt());
match readline { match readline {
Ok(line) => match interpret(line, ctrlc_recv.clone()) {
Ok(_) => {}
Ok(line) => match interpret(line, &mut config, ctrlc_recv.clone()) {
Ok(_) => prompt.style = config.prompt_style.clone(),
Err(ShellError::EmptyLine) => { Err(ShellError::EmptyLine) => {
continue; continue;
} }

1
src/prompt.rs

@ -1,5 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Clone)]
pub enum PromptStyle { pub enum PromptStyle {
None, None,
} }

Loading…
Cancel
Save