summaryrefslogtreecommitdiffstats
path: root/src/poem/recite.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/poem/recite.rs')
-rw-r--r--src/poem/recite.rs222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/poem/recite.rs b/src/poem/recite.rs
new file mode 100644
index 0000000..ba739c2
--- /dev/null
+++ b/src/poem/recite.rs
@@ -0,0 +1,222 @@
+mod ps;
+use super::Poem;
+use crate::path::prefresh;
+use crate::poem::elements::rune::Rune;
+use std::env;
+use std::process::exit;
+use std::{
+ io,
+ path::Path,
+ sync::{Arc, Mutex},
+};
+
+pub trait Reciteable {
+ fn recite(
+ &self,
+ path: &Vec<&Path>,
+ bins: &mut Vec<String>,
+ stdout: Option<bool>,
+ ) -> Result<String, io::Error>;
+}
+
+impl Reciteable for Poem {
+ fn recite(
+ &self,
+ path: &Vec<&Path>,
+ bins: &mut Vec<String>,
+ stdout: Option<bool>,
+ ) -> Result<String, io::Error> {
+ // Should we print to stdout or always capture it
+ let stdout = stdout.unwrap_or(true);
+
+ // Variable for storing the output of a piped verse
+ let mut out: String = String::new();
+
+ // Keep track of pids for background processes
+ let mut pids: Arc<Mutex<Vec<i32>>> = Arc::new(Mutex::new(Vec::new()));
+
+ // Loop through each verse in the poem
+ for verse in self.iter() {
+ // Verse may need to be mutable
+ let mut verse = verse.clone();
+
+ // Check for environment variables
+ for word in verse.stanza.iter_mut() {
+ while word.contains("$") {
+ let mut name = vec![];
+ let mut idx = word.chars().position(|c| c == '$').unwrap() + 1;
+ let bytes = word.as_bytes();
+ while idx < word.len() {
+ let c = bytes[idx] as char;
+ if !c.is_alphanumeric() {
+ break;
+ }
+ name.push(c);
+ idx += 1;
+ }
+ let name: String = format!("${}", name.iter().collect::<String>());
+ let envar = name[1..].to_string();
+ let envar = match env::var(envar) {
+ Ok(envar) => envar.to_string(),
+ Err(_) => "".to_string(),
+ };
+ *word = word.replace(name.as_str(), envar.as_str());
+ }
+ }
+
+ // Run interal poems
+ let v = verse.clone();
+ let mut new_stanza = None;
+ if verse.poems() {
+ // Collect all the words that have vertical tabs
+ let mut wordp_indicies = vec![];
+ let wordps = verse
+ .stanza
+ .iter_mut()
+ .enumerate()
+ .filter(|(_, w)| w.contains("\x0b"))
+ .map(|(i, w)| {
+ wordp_indicies.push(i + 1);
+ w
+ })
+ .collect::<Vec<&mut String>>();
+
+ // Loop through each word and replace with the output of the poem
+ let mut poems = verse.poems.iter();
+ let mut j = 0;
+ for wordp in wordps {
+ let times = wordp
+ .chars()
+ .filter(|c| c == &'\x0b')
+ .collect::<String>()
+ .len();
+ for _ in 0..times {
+ let poem = match poems.next() {
+ Some(poem) => poem,
+ None => break, // TODO: Return an error
+ };
+ let out = poem.recite(path, bins, Some(false))?;
+ if out.contains("\n") {
+ let mut out = out.split("\n");
+ let next = out.next().unwrap_or("").trim();
+ *wordp = wordp.replacen("\x0b", next, 1).to_string();
+ let (_, right) = v.stanza.split_at(wordp_indicies[j]);
+ let mut left = vec![];
+ let mut right = right.to_vec();
+ loop {
+ let next = match out.next() {
+ Some(next) => next,
+ None => break,
+ }
+ .to_string();
+ left.push(next);
+ }
+ left.append(&mut right);
+ new_stanza = Some(left.clone());
+ } else {
+ *wordp = wordp.replacen("\x0b", out.as_str(), 1).to_string();
+ }
+ }
+ j += 1;
+ }
+
+ // // Get indices of words in the verse that begin with a vertical tab
+ // let mut indicies = vec![];
+ // for (i, word) in verse.stanza.iter().enumerate() {
+ // if word.starts_with("\x0b") {
+ // indicies.push(i);
+ // }
+ // }
+
+ // // Try to recite the internal poem, and update the parent poem
+ // for (i, poem) in verse.poems.iter().enumerate() {
+ // let out = poem.recite(path, bins, Some(false))?;
+ // verse.stanza[indicies[i]] = out;
+ // }
+ }
+
+ match new_stanza {
+ Some(stanza) => {
+ let mut stanza = stanza.clone();
+ verse.stanza.append(&mut stanza);
+ }
+ None => {}
+ }
+
+ // Check if the user wants to exit the shell
+ if verse.verb() == "exit" || verse.verb() == "quit" {
+ exit(0);
+ }
+
+ // Check if the user wants to change directories
+ if verse.verb() == "cd" {
+ let path = match verse.clause() {
+ Some(path) => path[0].to_string(),
+ None => env!("HOME").to_string(),
+ };
+
+ match std::env::set_current_dir(&path) {
+ Ok(_) => continue,
+ Err(e) => {
+ eprintln!(
+ "cd: unable to change into {}: {}",
+ path,
+ e.to_string().to_lowercase()
+ );
+ continue;
+ }
+ }
+ }
+
+ // Check if the verb exists
+ // 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(bins) {
+ *bins = prefresh(path);
+ if !verse.spellcheck(bins) {
+ eprintln!("dwvsh: {}: command not found", verse.verb());
+
+ if verse.meter != Rune::And {
+ continue;
+ }
+ }
+ }
+
+ // Incant the verse, based on its meter
+ let status = if stdout {
+ match verse.io {
+ Rune::Read => Rune::incant_read(&mut verse, &mut out, &mut pids)?,
+ Rune::Write => Rune::incant_write(&mut verse, &mut out, &mut pids)?,
+ Rune::Addendum => Rune::incant_addendum(&mut verse, &mut out, &mut pids)?,
+ _ => match verse.meter {
+ Rune::None => Rune::incant_none(&verse, &mut out)?,
+ Rune::Couplet => Rune::incant_couplet(&verse, &mut out)?,
+ Rune::Quiet => Rune::incant_quiet(&verse, &mut out, &mut pids)?,
+ Rune::And => Rune::incant_and(&verse, &mut out)?,
+ Rune::Continue => Rune::incant_continue(&verse, &mut out)?,
+ _ => unreachable!(),
+ },
+ }
+ } else {
+ match verse.io {
+ Rune::Read => Rune::incant_read(&mut verse, &mut out, &mut pids)?,
+ Rune::Write => Rune::incant_write(&mut verse, &mut out, &mut pids)?,
+ Rune::Addendum => Rune::incant_addendum(&mut verse, &mut out, &mut pids)?,
+ _ => Rune::incant_couplet(&verse, &mut out)?,
+ }
+ };
+
+ // Break from the loop if the meter is not [Rune::Continue], and
+ // if the status is not 0
+ // For [Rune::Quiet], [Rune::incant_quiet] will always return 0
+ if verse.meter != Rune::Continue && status != 0 {
+ break;
+ }
+ }
+
+ // If we've successfully exited the loop, then all verses were properly
+ // recited
+ Ok(out.trim().to_string())
+ }
+}