From fedd4c31b0d1c6d036b1105a74b6e6a1f135f2b4 Mon Sep 17 00:00:00 2001
From: Rory Dudley
Date: Fri, 14 Jun 2024 20:29:32 -0400
Subject: Add back spellcheck

This commit reworks spellcheck() so it is more verbose about what it
returns. It also re-introduces the use of spellcheck() in
Poem::recite().

Spellcheck now returns a value in the enum, Spelling:
  FullPath -> Indicates a full path was specified as the verb
  OnPath -> Indicates the verb is on the $PATH
  BuiltIn -> Indicates the verb is a built-in command
  None (Option) -> The verb does not exist

This commit also removes some defunct (commented-out) code.
---
 src/poem/anthology.rs       | 41 +++---------------------------------
 src/poem/anthology/which.rs | 29 ++++++++++++++------------
 src/poem/elements/verse.rs  | 31 ++++++++++++++++++++-------
 src/poem/recite.rs          | 51 +++++++++++++++++++--------------------------
 4 files changed, 64 insertions(+), 88 deletions(-)

(limited to 'src')

diff --git a/src/poem/anthology.rs b/src/poem/anthology.rs
index a35eafb..086864c 100644
--- a/src/poem/anthology.rs
+++ b/src/poem/anthology.rs
@@ -29,38 +29,6 @@ pub fn lookup(verb: &str) -> Option<usize> {
     INDEX.iter().position(|v| v.to_string() == verb)
 }
 
-/// Run a builtin command, based on its verb
-///
-/// Use [lookup] to check if a verb is in the anthology's index (i.e. is a
-/// builtin), then call this with the current verse, the index found by
-/// [lookup], and the shell's global environment state.
-///
-/// # Example
-/// ```
-/// let index = anthology::lookup(verse.verb());
-/// if index.is_some() {
-///     anthology::incant(&verse, index.unwrap(), env);
-/// } else {
-///     // Run an external command
-///     ...
-/// }
-/// ```
-// pub fn incant(verse: &Verse, out: &mut Vec<u8>, index: usize, env: &mut Environment) -> i32 {
-//     // let verb = INDEX[index];
-//     // match verb {
-//     //     "alias" => alias::incant(verse, out, &mut env.aliases),
-//     //     "cd" => cd::incant(verse),
-//     //     "exit" => exit::incant(),
-//     //     "export" => export::incant(verse),
-//     //     "source" => source::incant(verse, out, env),
-//     //     "unalias" => alias::unincant(verse, &mut env.aliases),
-//     //     "unset" => export::unincant(verse),
-//     //     "which" => which::incant(verse, out, env),
-//     //     _ => unreachable!(),
-//     // }
-//     0
-// }
-
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub struct AnthologyStdin {
     data: Vec<u8>,
@@ -101,9 +69,9 @@ impl Anthology {
     /// ```
     /// let mut command = Anthology::new("alias");
     /// ```
-    pub fn new(verb: String) -> Self {
+    pub fn new(i: usize) -> Self {
         Anthology {
-            verb,
+            verb: INDEX[i].to_string(),
             clause: None,
             uin: false,
             uout: false,
@@ -148,11 +116,8 @@ impl Anthology {
     }
 
     pub fn spawn(&mut self, env: &mut Environment) -> Result<Self, io::Error> {
-        let index = lookup(self.verb.as_str()).unwrap();
-        let verb = INDEX[index];
-
         // Incant the built-in and set the output
-        self.output = Some(match verb {
+        self.output = Some(match self.verb.as_str() {
             "alias" => alias::incant(&self.clause, self.uout, &mut env.aliases),
             "cd" => cd::incant(&self.clause, self.uerr),
             "exit" => exit::incant(),
diff --git a/src/poem/anthology/which.rs b/src/poem/anthology/which.rs
index 6fe709c..0872dda 100644
--- a/src/poem/anthology/which.rs
+++ b/src/poem/anthology/which.rs
@@ -1,4 +1,5 @@
 use crate::compose::Environment;
+use crate::poem::elements::verse::Spelling;
 use crate::poem::Verse;
 use std::os::unix::process::ExitStatusExt;
 use std::process::{ExitStatus, Output};
@@ -22,26 +23,28 @@ pub fn incant(clause: &Option<Vec<String>>, uout: bool, uerr: bool, env: &Enviro
                     continue;
                 }
 
-                // Check if it's a built-in
-                match super::lookup(&word) {
-                    Some(_) => {
+                // Manually check the path
+                let mut verb = Verse::new();
+                verb.push(word.clone());
+                match verb.spellcheck(&env.bins) {
+                    Some(Spelling::OnPath(i)) => {
+                        output = format!("{}\n", env.bins[i]);
+                        if uout {
+                            out.append(&mut output.as_bytes().to_vec());
+                        } else {
+                            print!("{}", output);
+                        }
+                    }
+                    Some(Spelling::BuiltIn(_)) => {
                         output = format!("{}: shell built-in command\n", word);
                         if uout {
                             out.append(&mut output.as_bytes().to_vec());
                         } else {
                             print!("{}", output);
                         }
-                        continue;
                     }
-                    None => {}
-                }
-
-                // Manually check the path
-                let mut verb = Verse::new();
-                verb.push(word.clone());
-                match verb.spellcheck(&env.bins) {
-                    Some(i) => {
-                        output = format!("{}\n", env.bins[i]);
+                    Some(Spelling::FullPath) => {
+                        output = format!("{}\n", word);
                         if uout {
                             out.append(&mut output.as_bytes().to_vec());
                         } else {
diff --git a/src/poem/elements/verse.rs b/src/poem/elements/verse.rs
index 42d7c6a..b4f41ba 100644
--- a/src/poem/elements/verse.rs
+++ b/src/poem/elements/verse.rs
@@ -1,6 +1,7 @@
 use super::rune::Rune;
 use super::stanza::Stanza;
 use super::word::Word;
+use crate::poem::anthology;
 use crate::poem::anthology::Anthology;
 use crate::poem::Poem;
 mod logic;
@@ -32,6 +33,13 @@ pub struct Verse {
     pub meter: Rune,
 }
 
+#[derive(Debug, Clone)]
+pub enum Spelling {
+    FullPath,
+    OnPath(usize),
+    BuiltIn(usize),
+}
+
 pub trait Forkable<T> {
     fn fork(&mut self, env: &mut Environment) -> Result<T, io::Error>;
 }
@@ -216,7 +224,7 @@ impl Verse {
     /// stanza_success.spellcheck(bins) // -> Some(i)
     /// stanza_fail.spellcheck(bins) // -> None
     /// ```
-    pub fn spellcheck(&self, bins: &Vec<String>) -> Option<usize> {
+    pub fn spellcheck(&self, bins: &Vec<String>) -> Option<Spelling> {
         // An empty verb (i.e. the empty string) cannot be a program, so
         // return false
         // Thanks to the parsing in Poem::read, however, it's
@@ -225,6 +233,13 @@ impl Verse {
             return None;
         }
 
+        // Check for built-ins
+        let i = anthology::lookup(self.verb().as_str());
+        match i {
+            Some(i) => return Some(Spelling::BuiltIn(i)),
+            None => {}
+        };
+
         // Only search the $PATH if a full or relative path was not given, or
         // if the path given does not exist
         if !Path::new(self.verb().as_str()).exists() {
@@ -232,7 +247,7 @@ impl Verse {
             // Searches in $PATH order
             for (i, path) in bins.iter().enumerate() {
                 if path.split('/').last().unwrap() == self.verb() {
-                    return Some(i);
+                    return Some(Spelling::OnPath(i));
                 }
             }
 
@@ -241,7 +256,7 @@ impl Verse {
         }
 
         // Return the length of bins if the full path or relative path exists
-        Some(bins.len())
+        Some(Spelling::FullPath)
     }
 
     /// Run a command
@@ -256,7 +271,7 @@ impl Verse {
         out: &mut Vec<u8>,
         pids: &mut Arc<Mutex<Vec<i32>>>,
         env: &mut Environment,
-        anthology: Option<usize>,
+        spell: Option<Spelling>,
     ) -> Result<i32, io::Error> {
         // Read files into 'out' if Rune::Read is present in the verse's IO
         if self.io.contains(&Rune::Read) {
@@ -274,12 +289,12 @@ impl Verse {
         }
 
         // Build and run the command
-        match anthology {
-            Some(_) => {
-                let mut command = Anthology::new(self.verb());
+        match spell {
+            Some(Spelling::BuiltIn(i)) => {
+                let mut command = Anthology::new(i);
                 incant!(self.verb(), command, out, pids, env, self)
             }
-            None => {
+            _ => {
                 let mut command = Command::new(self.verb());
                 incant!(self.verb(), command, out, pids, env, self)
             }
diff --git a/src/poem/recite.rs b/src/poem/recite.rs
index b33cc87..b27ef75 100644
--- a/src/poem/recite.rs
+++ b/src/poem/recite.rs
@@ -1,5 +1,6 @@
 use super::Poem;
 use crate::compose::Environment;
+use crate::path;
 use crate::poem::anthology;
 use crate::poem::elements::rune::Rune;
 use crate::poem::elements::stanza::Stanza;
@@ -141,36 +142,28 @@ impl Reciteable for Poem {
                 None => {}
             };
 
+            // Check if the verse's verb exists
+            let mut spell = None;
+            if !verse.verb().is_empty() {
+                // Check if the verb exists on the PATH
+                // If it doesn't exist, try refreshing the binary cache, and check again
+                // If it still doesn't exist, print an error
+                spell = verse.spellcheck(&env.bins);
+                if !spell.is_some() {
+                    env.bins = path::refresh();
+                    spell = verse.spellcheck(&env.bins);
+                    if !spell.is_some() {
+                        eprintln!("dwvsh: {}: command not found", verse.verb());
+
+                        if verse.meter != Rune::And {
+                            continue;
+                        }
+                    }
+                }
+            }
+
             // Incant the verse if it's a built-in
-            let status =
-                verse.incant(&mut out, &mut pids, env, anthology::lookup(&verse.verb()))?;
-            // let status = if index.is_some() {
-            //     anthology::incant(&verse, &mut out, index.unwrap(), env)
-            // } else {
-            //     // Checking for environment variables and running internal
-            //     // poems may mean that the verb is empty now, so check it once
-            //     // more
-            //     // If it is empty, just continue to the next verse
-            //     if !verse.verb().is_empty() {
-            //         // Check if the verb exists on the PATH
-            //         // If it doesn't exist, try refreshing the binary cache, and check
-            //         // again
-            //         // If it still doesn't exist, print an error
-            //         if !verse.spellcheck(&env.bins).is_some() {
-            //             env.bins = path::refresh();
-            //             if !verse.spellcheck(&env.bins).is_some() {
-            //                 eprintln!("dwvsh: {}: command not found", verse.verb());
-            //
-            //                 if verse.meter != Rune::And {
-            //                     continue;
-            //                 }
-            //             }
-            //         }
-            //     } else {
-            //         continue;
-            //     }
-            //     verse.incant(&mut out, &mut pids, anthology::lookup(&verse.verb()))?
-            // };
+            let status = verse.incant(&mut out, &mut pids, env, spell)?;
 
             // Break from the loop if the meter is not [Rune::Continue], and
             // if the status is not 0
-- 
cgit v1.2.3