diff options
-rw-r--r-- | doc/rc.pdf | bin | 0 -> 79087 bytes | |||
-rw-r--r-- | src/commandline.rs (renamed from src/parser.rs) | 31 | ||||
-rw-r--r-- | src/commandline/builtins.rs | 45 | ||||
-rw-r--r-- | src/commandline/command.rs (renamed from src/parser/command.rs) | 81 | ||||
-rw-r--r-- | src/main.rs | 23 |
5 files changed, 91 insertions, 89 deletions
diff --git a/doc/rc.pdf b/doc/rc.pdf Binary files differnew file mode 100644 index 0000000..6feab51 --- /dev/null +++ b/doc/rc.pdf diff --git a/src/parser.rs b/src/commandline.rs index 0c771f9..489eaa8 100644 --- a/src/parser.rs +++ b/src/commandline.rs @@ -7,20 +7,17 @@ use std::process::ExitStatus; // || on prev cmd fail mod command; -use command::{CommandInfo, RunOn, RunResult}; +use command::{Command, RunIf, RunResult}; +mod builtins; -pub struct CommandLine(Vec<CommandInfo>); +pub struct CommandLine(Vec<Command>); impl CommandLine { pub fn new(line: &str) -> Self { CommandLine(split(line)) } - pub fn run( - &self, - home: &PathBuf, - mut status: Option<ExitStatus>, - ) -> Result<Option<ExitStatus>, Error> { + pub fn run(&self,home: &PathBuf, mut status: Option<ExitStatus>) -> Result<Option<ExitStatus>, Error> { for cmd in &self.0 { if cmd.when.can_run(&status) { match cmd.run(&home, &status)? { @@ -33,14 +30,14 @@ impl CommandLine { } } -fn split(line: &str) -> Vec<CommandInfo> { - let mut r: Vec<CommandInfo> = Vec::new(); +fn split(line: &str) -> Vec<Command> { + let mut r: Vec<Command> = Vec::new(); let mut next = line.chars().peekable(); let mut iter = line.chars().enumerate(); let mut start_idx = 0; - let mut state = RunOn::Always; + let mut state = RunIf::Always; while let Some((i, v)) = { next.next(); @@ -49,23 +46,23 @@ fn split(line: &str) -> Vec<CommandInfo> { let n = next.peek(); if v == ';' { - r.push(CommandInfo::new(&line[start_idx..i], state)); - state = RunOn::Always; + r.push(Command::new(&line[start_idx..i], state)); + state = RunIf::Always; start_idx = i + 1; } else if v == '|' && n == Some(&'|') { - r.push(CommandInfo::new(&line[start_idx..i], state)); - state = RunOn::ExitFailure; + r.push(Command::new(&line[start_idx..i], state)); + state = RunIf::ExitFailure; start_idx = i + 2; next.next(); iter.next(); } else if v == '&' && n == Some(&'&') { - r.push(CommandInfo::new(&line[start_idx..i], state)); - state = RunOn::ExitSuccess; + r.push(Command::new(&line[start_idx..i], state)); + state = RunIf::ExitSuccess; start_idx = i + 2; next.next(); iter.next(); } else if n == None { - r.push(CommandInfo::new(&line[start_idx..], state)); + r.push(Command::new(&line[start_idx..], state)); } } diff --git a/src/commandline/builtins.rs b/src/commandline/builtins.rs new file mode 100644 index 0000000..c5999f2 --- /dev/null +++ b/src/commandline/builtins.rs @@ -0,0 +1,45 @@ +use std::io::{Error, ErrorKind::InvalidInput}; +use std::path::{Path, PathBuf}; +use super::command::RunResult; + +pub(in crate::commandline) fn cd(args: &[String], home: &PathBuf) -> Result<RunResult, Error> { + if args.len() > 1 { + return Err(Error::new( + InvalidInput, + "Too many arguments passed to cd", + )); + } + + let root = if args.len() == 0 { + home.to_path_buf() + } else { + std::fs::canonicalize(Path::new(args[0].as_str()))? + }; + + match std::env::set_current_dir(&root) { + Ok(_) => Ok(RunResult::Builtin), + Err(e) => Err(e), + } +} + +pub(in crate::commandline) fn set(args: &[String]) -> Result<RunResult, Error> { + if args.len() != 2 { + return Err(Error::new( + InvalidInput, + format!("set requires 2 arguments, got {}", args.len()), + )); + } + std::env::set_var(&args[0], &args[1]); + Ok(RunResult::Builtin) +} + +pub(in crate::commandline) fn unset(args: &[String]) -> Result<RunResult, Error> { + if args.len() != 1 { + return Err(Error::new( + InvalidInput, + format!("unset requires 1 argument, got {}", args.len()), + )); + } + std::env::remove_var(&args[0]); + Ok(RunResult::Builtin) +} diff --git a/src/parser/command.rs b/src/commandline/command.rs index 4dea8a6..d00f420 100644 --- a/src/parser/command.rs +++ b/src/commandline/command.rs @@ -1,6 +1,8 @@ -use std::io::{Error, ErrorKind}; -use std::path::{Path, PathBuf}; -use std::process::{Command, ExitStatus}; +use std::io::{Error}; +use std::path::{PathBuf}; +use std::process::ExitStatus; +use std::process::Command as Process; +use super::builtins::{cd, set, unset}; // > overwrite // >> append @@ -14,46 +16,42 @@ enum Redirect { } #[derive(Debug, PartialEq, Copy, Clone)] -pub(in crate::parser) enum RunOn { - Always, - ExitSuccess, - ExitFailure, -} +pub(in crate::commandline) enum RunIf { Always, ExitSuccess, ExitFailure } -impl RunOn { - pub(in crate::parser) fn can_run(&self, status: &Option<ExitStatus>) -> bool { - if *self == RunOn::Always { +impl RunIf { + pub(in crate::commandline) fn can_run(&self, status: &Option<ExitStatus>) -> bool { + if *self == RunIf::Always { return true; } match status { Some(s) => { - (*self == RunOn::ExitSuccess && s.success()) - || (*self == RunOn::ExitFailure && !s.success()) + (*self == RunIf::ExitSuccess && s.success()) + || (*self == RunIf::ExitFailure && !s.success()) } None => true, } } } -pub(in crate::parser) enum RunResult { +pub(in crate::commandline) enum RunResult { Command(ExitStatus), Builtin, } #[derive(Debug)] -pub struct CommandInfo { +pub struct Command { args: Vec<String>, stdout: Redirect, - pub(in crate::parser) when: RunOn, + pub(in crate::commandline) when: RunIf, } -impl CommandInfo { - pub(in crate::parser) fn new(line: &str, when: RunOn) -> CommandInfo { +impl Command { + pub(in crate::commandline) fn new(line: &str, when: RunIf) -> Command { let (args, stdout) = tokenize(line); - CommandInfo { args, stdout, when } + Command { args, stdout, when } } - pub(in crate::parser) fn run( + pub(in crate::commandline) fn run( &self, home: &PathBuf, status: &Option<ExitStatus>, @@ -70,7 +68,7 @@ impl CommandInfo { "set" => set(&self.args[1..]), "unset" => unset(&self.args[1..]), _ => { - let mut child = Command::new(&self.args[0]).args(&self.args[1..]).spawn()?; + let mut child = Process::new(&self.args[0]).args(&self.args[1..]).spawn()?; Ok(RunResult::Command(child.wait().unwrap())) } } @@ -161,44 +159,3 @@ fn test_tokenizer() { println!("{:?}", tokenize("ls -l something >>'junction jan''s'")); } */ -fn cd(args: &[String], home: &PathBuf) -> Result<RunResult, Error> { - if args.len() > 1 { - return Err(Error::new( - ErrorKind::InvalidInput, - "Too many arguments passed to cd", - )); - } - - let root = if args.len() == 0 { - home.to_path_buf() - } else { - std::fs::canonicalize(Path::new(args[0].as_str()))? - }; - - match std::env::set_current_dir(&root) { - Ok(_) => Ok(RunResult::Builtin), - Err(e) => Err(e), - } -} - -fn set(args: &[String]) -> Result<RunResult, Error> { - if args.len() != 2 { - return Err(Error::new( - ErrorKind::InvalidInput, - format!("set requires 2 arguments, got {}", args.len()), - )); - } - std::env::set_var(&args[0], &args[1]); - Ok(RunResult::Builtin) -} - -fn unset(args: &[String]) -> Result<RunResult, Error> { - if args.len() != 1 { - return Err(Error::new( - ErrorKind::InvalidInput, - format!("unset requires 1 argument, got {}", args.len()), - )); - } - std::env::remove_var(&args[0]); - Ok(RunResult::Builtin) -} diff --git a/src/main.rs b/src/main.rs index 95c3c46..ca5dc00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,21 @@ use rustyline::error::ReadlineError; use rustyline::Editor; -mod parser; +mod commandline; +use commandline::CommandLine; mod prompt; -fn main() { - match std::fs::read_to_string("/etc/motd") { - Ok(motd) => print!("{}", motd), - Err(_) => {} +fn main() -> Result<(), std::io::Error>{ + if let Ok(motd) = std::fs::read_to_string("/etc/motd") { + print!("{}", motd) } - let prompt = prompt::Prompt::new().unwrap(); - let mut status = None; - - // `()` can be used when no completer is required + // TODO: [completer] `()` can be used when no completer is required let mut rl = Editor::<()>::new(); + let prompt = prompt::Prompt::new()?; + + let mut status = None; // exit status of last command + // map of variables /*if rl.load_history("history.txt").is_err() { println!("No previous history."); @@ -25,7 +26,7 @@ fn main() { Ok(line) => { rl.add_history_entry(line.as_str()); - let cmd = parser::CommandLine::new(&line); + let cmd = CommandLine::new(&line); match cmd.run(&prompt.home, status) { Ok(s) => status = s, Err(e) => eprintln!("{}", e), @@ -47,4 +48,6 @@ fn main() { } //rl.save_history("history.txt").unwrap(); + + Ok(()) } |