diff options
Diffstat (limited to 'src/poem/recite.rs')
-rw-r--r-- | src/poem/recite.rs | 222 |
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()) + } +} |