summaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authorRory Dudley2024-02-22 23:12:24 -0700
committerRory Dudley2024-02-22 23:12:24 -0700
commit536e250653e5c140a6d9e60f1cd652b79135e7a6 (patch)
tree6a6b212c913f52d559ed9ea8a9477bebb9b38a2d /src/main.rs
parenta14fdf8faa85628baf06399961eb1f9ab4c5f3eb (diff)
downloaddwarvish-536e250653e5c140a6d9e60f1cd652b79135e7a6.tar.gz
Reorganization and comments
Broke out the structs for a poem into their own file: src/recite.rs. Also put the 'prefresh' function into it's own file: src/recite/path.rs. Commented most of the parser code (including structs and helper methods related to parsing (i.e. Verse, Stanza, Meter, Poem)). Renamed any instance of the 'paths' variable to 'path'.
Notes
Notes: The biggest task now is to cleanup Poem::recite. It has a ton of bogus error messages, and (seemingly) redundant code.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs441
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);
}