diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 441 |
1 files changed, 21 insertions, 420 deletions
diff --git a/src/main.rs b/src/main.rs index 3c016e9..db349d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,403 +1,9 @@ -use core::fmt; +mod recite; use ctrlc; -use std::fs; +use recite::path::prefresh; +use recite::Poem; use std::io::{self, Write}; use std::path::Path; -use std::process::{exit, Command, Stdio}; - -#[derive(Debug)] -enum Meter { - None, // No meter - Pipe, // Pipe the output of this command into the next - Daemon, // Fork the command into the background - And, // Run the next command only if this succeeds - String, // Run the next command, even if this doesn't succeed -} - -impl fmt::Display for Meter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let meter = match self { - Meter::None => "", - Meter::Pipe => "|", - Meter::Daemon => "&", - Meter::And => "&&", - Meter::String => ";", - }; - - write!(f, "{}", meter) - } -} - -#[derive(Debug)] -struct Stanza { - verb: String, - clause: Vec<String>, -} - -impl Stanza { - fn new(stanza: Vec<String>) -> Stanza { - Stanza { - verb: stanza[0].clone(), - clause: stanza[1..].to_vec(), - } - } - - fn spellcheck(&self, bins: &Vec<String>) -> bool { - if self.verb.is_empty() { - return false; - } - - if !Path::new(self.verb.as_str()).exists() { - match bins - .iter() - .find(|bin| bin.split('/').last().unwrap() == self.verb) - { - Some(_) => return true, - None => return false, - } - } - - true - } -} - -#[derive(Debug)] -struct Verse { - stanza: Stanza, - meter: Meter, - stdin: bool, -} - -impl Verse { - fn new(stanza: Stanza, meter: Meter, stdin: bool) -> Verse { - Verse { - stanza, - meter, - stdin, - } - } - - fn spellcheck(&self, bins: &Vec<String>) -> bool { - self.stanza.spellcheck(bins) - } - - fn verb(&self) -> String { - self.stanza.verb.clone() - } - - fn clause(&self) -> Vec<String> { - self.stanza.clause.clone() - } -} - -impl fmt::Display for Verse { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.verb(), self.clause().join(" ")) - } -} - -#[derive(Debug)] -struct Poem { - verses: Vec<Verse>, -} - -impl Poem { - fn new(verses: Vec<Verse>) -> Poem { - Poem { verses } - } - - fn recite(&self, paths: &Vec<&Path>, bins: &mut Vec<String>) -> bool { - // println!("{:#?}", self); - let mut out: String = String::new(); - - for verse in self.verses.iter() { - // Check if user wants to exit the shell - if verse.verb() == "exit" || verse.verb() == "quit" { - exit(0); - } - - if verse.verb() == "cd" { - let path: String; - if verse.clause().is_empty() { - path = env!("HOME").to_string(); - } else { - path = verse.clause().first().unwrap().to_owned(); - } - - match std::env::set_current_dir(&path) { - Ok(_) => continue, - Err(_) => { - println!("cd: unable to change into {}", path); - continue; - } - } - } - - if !verse.spellcheck(bins) { - *bins = prefresh(paths); - if !verse.spellcheck(bins) { - println!("dwvsh: {}: command not found", verse.verb()); - continue; - } - } - - if verse.stdin { - match verse.meter { - Meter::Pipe => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("dwvsh: error 0"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 6"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 7"); - out.clear(); - - let output = child.wait_with_output().unwrap(); - // out = String::from_utf8_lossy(&output.stdout).to_string(); - out = String::from_utf8(output.stdout).unwrap(); - } - Meter::Daemon => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .stdin(Stdio::piped()) - .spawn() - .expect("dwvsh: error 1"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 8"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 9"); - out.clear(); - - print!("[f] {}", child.id()); - // let p = prompt.to_owned(); - std::thread::spawn(move || { - child.wait().unwrap(); - println!("[f] +done {}", child.id()); - io::stdout().flush().unwrap(); - }); - } - Meter::String => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 5"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 8"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 9"); - out.clear(); - - child.wait().unwrap(); - } - Meter::And | Meter::None => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .stdin(Stdio::piped()) - .spawn() - .expect("dwvsh: error 2"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 10"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 11"); - out.clear(); - - if !child.wait().unwrap().success() { - break; - } - } - }; - } else { - match verse.meter { - Meter::Pipe => { - let child = Command::new(verse.verb()) - .args(verse.clause()) - .stdout(Stdio::piped()) - .spawn() - .expect("dwvsh: error 3"); - - let output = child.wait_with_output().unwrap(); - out = String::from_utf8_lossy(&output.stdout).to_string(); - // out = String::from_utf8(output.stdout).unwrap(); - } - Meter::Daemon => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 4"); - - println!("[f] {}", child.id()); - std::thread::spawn(move || { - child.wait().unwrap(); - print!("[f] +done {}\n", child.id()); - io::stdout().flush().unwrap(); - }); - } - Meter::String => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 5"); - - child.wait().unwrap(); - } - Meter::And | Meter::None => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 5"); - - if !child.wait().unwrap().success() { - break; - } - } - }; - } - } - - true - } -} - -fn read(poetry: String) -> Option<Poem> { - let mut chars = poetry.chars(); - let mut verses: Vec<Verse> = Vec::new(); - let mut stanza: Vec<String> = Vec::new(); - let mut word: Vec<char> = Vec::new(); - let mut prev: Option<&Verse> = None; - - loop { - let char = chars.next(); - - let pipe = match prev { - Some(prev) => match prev.meter { - Meter::Pipe => true, - Meter::Daemon | Meter::And | Meter::String | Meter::None => false, - }, - None => false, - }; - - let metered = match prev { - Some(prev) => match prev.meter { - Meter::Pipe | Meter::Daemon | Meter::And | Meter::String => true, - Meter::None => false, - }, - None => false, - }; - - match char { - Some(meter) - if (meter == '|' || meter == '&' || meter == ';') - && metered - && stanza.is_empty() => - { - println!("dwvsh: parse error"); - return None; - } - Some(meter) if meter == '|' => { - if !word.is_empty() { - stanza.push(word.iter().collect()); - } - verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Pipe, pipe)); - stanza = Vec::new(); - word.clear(); - } - Some(meter) if meter == '&' => { - if !word.is_empty() { - stanza.push(word.iter().collect()); - } - - match chars.clone().peekable().peek() { - Some(c) if c == &'&' => { - chars.next(); - verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::And, pipe)); - } - Some(_) => { - verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Daemon, pipe)); - } - None => { - verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Daemon, pipe)); - } - } - - stanza = Vec::new(); - word.clear(); - } - Some(meter) if meter == ';' => { - if !word.is_empty() { - stanza.push(word.iter().collect()); - } - verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::String, pipe)); - stanza = Vec::new(); - word.clear(); - } - Some(char) if char == ' ' => { - if !word.is_empty() { - stanza.push(word.iter().collect()); - word.clear(); - } - } - Some(char) => { - word.push(char); - } - None => { - if !word.is_empty() { - stanza.push(word.iter().collect()); - } - if !stanza.is_empty() { - verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::None, pipe)); - } - break; - } - } - - prev = match verses.last() { - Some(verse) => Some(verse), - None => None, - }; - } - - Some(Poem::new(verses)) -} - -/// Refresh the shell's $PATH -/// -/// This function caches all valid paths within within the directories -/// specified. -/// -/// # Arguments -/// * `paths` - A reference to a vector that holds a list to the shell $PATHs -/// -/// # Returns -/// * `bins: Vec<String>` - A new cache of all valid file paths in $PATH -/// -/// # Examples -/// ``` -/// let paths = vec!["/bin"]; -/// let paths = paths.into_iter().map(Path::new).collect(); -/// let mut bins = prefresh(&paths); -/// ... -/// // A situation occurs where the $PATH needs to be refreshed -/// bins = prefresh(&paths) -/// ``` -fn prefresh(paths: &Vec<&Path>) -> Vec<String> { - let mut bins: Vec<String> = Vec::new(); - - for path in paths { - let files = fs::read_dir(path).expect( - format!( - "dwvsh: error: unable to read the contents of {}", - path.display().to_string() - ) - .as_str(), - ); - - for file in files { - bins.push(file.unwrap().path().display().to_string()); - } - } - - bins -} /// Starts the main shell loop /// @@ -408,16 +14,16 @@ fn prefresh(paths: &Vec<&Path>) -> Vec<String> { /// # Examples /// ``` /// fn main() { -/// let paths = vec!["/bin"]; -/// let paths = paths.into_iter().map(Path::new).collect(); +/// let path = vec!["/bin"]; +/// let path = path.into_iter().map(Path::new).collect(); /// let prompt = "|> "; /// ... -/// repl(&paths, prompt); +/// repl(&path, prompt); /// } /// ``` -fn repl(paths: &Vec<&Path>, prompt: &str) { +fn repl(path: &Vec<&Path>, prompt: &str) { // Initial path refresh on startup - let mut bins: Vec<String> = prefresh(paths); + let mut bins: Vec<String> = prefresh(path); // Main shell loop loop { @@ -442,16 +48,17 @@ fn repl(paths: &Vec<&Path>, prompt: &str) { let poetry = String::from(poetry.trim()); // Skip parsing if there is no poetry - if !poetry.is_empty() { - // Parse a poem - let poem = read(poetry); - match poem { - Some(poem) => { - // poem.recite(paths, &mut bins, prompt); - poem.recite(paths, &mut bins); - } - None => {} + if poetry.is_empty() { + continue; + } + + // Parse a poem + let poem = Poem::read(poetry); + match poem { + Some(poem) => { + poem.recite(path, &mut bins); } + None => {} } } } @@ -462,7 +69,7 @@ fn repl(paths: &Vec<&Path>, prompt: &str) { fn main() { // Define paths // TODO: Hardcoded path should only be the fallback - let paths = vec![ + let path = vec![ "/bin", "/sbin", "/usr/bin", @@ -470,7 +77,7 @@ fn main() { "/usr/local/bin", "/usr/local/sbin", ]; - let paths = paths.into_iter().map(Path::new).collect(); + let path = path.into_iter().map(Path::new).collect(); // Set the prompt let prompt = "|> "; @@ -482,12 +89,6 @@ fn main() { }) .expect("dwvsh: signals: unable to set sigint handler"); - // let poem = read("eza -la".to_string()); - // for line in poem.verses.iter().zip(poem.meters) { - // let (verse, meter) = line; - // println!("{}: {}", meter, verse); - // } - // Begin evaluating commands - repl(&paths, prompt); + repl(&path, prompt); } |