diff options
Diffstat (limited to 'src/poem/elements/rune.rs')
-rw-r--r-- | src/poem/elements/rune.rs | 339 |
1 files changed, 23 insertions, 316 deletions
diff --git a/src/poem/elements/rune.rs b/src/poem/elements/rune.rs index 2307519..1322ca5 100644 --- a/src/poem/elements/rune.rs +++ b/src/poem/elements/rune.rs @@ -1,14 +1,4 @@ -use super::verse::Verse; -use crate::iobtask; -use crate::{btask, ctask, task}; use core::fmt; -use libc::waitpid; -use libc::WNOHANG; -use std::fs::OpenOptions; -use std::io::{self, Read, Write}; -use std::os::unix::process::CommandExt; -use std::process::{Command, Stdio}; -use std::sync::{Arc, Mutex}; /// Describes one or two characters from the input /// @@ -40,21 +30,25 @@ use std::sync::{Arc, Mutex}; /// * `Else` - Any other character #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Rune { - None, // No meter (the end of a poem) - Pause, // A space - Path, // A forward slash - Remark, // A comment - String, // Interpret the following as one large [Word] - Poem, // Run a sub-poem before the main one - Read, // Read files into STDIN - Write, // Send STDOUT to a file - Addendum, // Append STDOUT to a file - Couplet, // Pipe the output of this command into the next - Quiet, // Fork the command into the background - And, // Run the next command only if this succeeds - Continue, // Run the next command, even if this doesn't succeed - Home, // Interpret '~' as $HOME - Else, // Any other character + None, // No meter (the end of a poem) + Pause, // A space + Path, // A forward slash + Remark, // A comment + String, // Interpret the following as one large [Word] + Poem, // Run a sub-poem before the main one + Read, // Read files into STDIN + Write, // Send STDOUT to a file + Write2, // Send STDERR to a file + WriteAll, // Send STDOUT and STDERR to a file + Addendum, // Append STDOUT to a file + Addendum2, // Append STDERR to a file + AddendumAll, // Append STDOUT and STDERR to a file + Couplet, // Pipe the output of this command into the next + Quiet, // Fork the command into the background + And, // Run the next command only if this succeeds + Continue, // Run the next command, even if this doesn't succeed + Home, // Interpret '~' as $HOME + Else, // Any other character } impl fmt::Display for Rune { @@ -71,7 +65,11 @@ impl fmt::Display for Rune { Rune::Poem => "`", Rune::Read => "<", Rune::Write => ">", + Rune::Write2 => "2>", + Rune::WriteAll => "&>", Rune::Addendum => ">>", + Rune::Addendum2 => "2>>", + Rune::AddendumAll => "&>>", Rune::Couplet => "|", Rune::Quiet => "&", Rune::And => "&&", @@ -83,294 +81,3 @@ impl fmt::Display for Rune { write!(f, "{}", rune) } } - -impl Rune { - /// Recite a verse with [Rune::None] - /// - /// Call this function on a [Verse] with a meter of type [Rune::None]. - /// This forks into a child process, calls the `verb()` (i.e. program) - /// that was specified in the [Verse], then waits for that program to - /// complete. If the last [Verse] piped its contents into `out`, it will - /// be piped into the STDIN of this [Verse]. If all Rust code is called - /// successfully, return the exit code of the process. Otherwise, return a - /// [std::io::Error]. - /// - /// # Arguments - /// * `verse: &Verse` - The verse to recite - /// * `out: &mut String` - A string that may have output from the last command - pub fn incant_none(verse: &Verse, out: &mut Vec<u8>) -> Result<i32, io::Error> { - let child = task!(verse, out); - - let output = child.wait_with_output()?; - - if !output.status.success() { - return Ok(output.status.code().unwrap_or(-1)); - } - - Ok(output.status.code().unwrap_or(0)) - } - - /// Recite a verse with [Rune::Couplet] - /// - /// Call this function on a [Verse] with a meter of type [Rune::Couplet]. - /// This forks into a child process, calls the `verb` (i.e. program) - /// that was specified in the [Verse], then waits for that program to - /// complete. If the last [Verse] piped its contents into `out`, it will - /// be piped into the STDIN of this [Verse]. Then, the contents of this - /// processes' STDOUT are stored in `out`. If all Rust code is called - /// successfully, return the exit code of the process. Otherwise, return a - /// [std::io::Error]. - /// - /// # Arguments - /// * `verse: &Verse` - The verse to recite - /// * `out: &mut String` - A string that may have output from the last command - pub fn incant_couplet(verse: &Verse, out: &mut Vec<u8>) -> Result<i32, io::Error> { - let child = ctask!(verse, out); - - let mut output = child.wait_with_output()?; - - if !output.status.success() { - return Ok(output.status.code().unwrap_or(-1)); - } - - out.append(&mut output.stdout); - - Ok(output.status.code().unwrap_or(0)) - } - - /// Recite a verse with [Rune::Quiet] - /// - /// Call this function on a [Verse] with a meter of type [Rune::Quiet]. - /// This forks a child process into the background. It then registers a - /// `SIGCHLD` handler, making sure to do so for each PID in the `pids` - /// Vec. If the last [Verse] piped its contents into `out`, it will be - /// piped into the STDIN of this [Verse]. If all Rust code is called - /// successfully, return the exit code of the process. Otherwise, return a - /// [std::io::Error]. - /// - /// # Arguments - /// * `verse: &Verse` - The verse to recite - /// * `out: &mut String` - A string that may have output from the last command - /// * `pids: Arc<Mutex<Vec<i32>>>` - A vector that stores the PIDs of all background processes that belong to the shell - pub fn incant_quiet( - verse: &Verse, - out: &mut Vec<u8>, - pids: &mut Arc<Mutex<Vec<i32>>>, - ) -> Result<i32, io::Error> { - let child = btask!(verse, out); - println!("[&] {}", child.id()); - - pids.lock().unwrap().push(child.id() as i32); - let stanza = verse.stanza.join(" ").to_string(); - let pids = Arc::clone(pids); - - unsafe { - signal_hook::low_level::register(signal_hook::consts::SIGCHLD, move || { - for pid in pids.lock().unwrap().iter() { - let mut pid = *pid; - let mut status: i32 = 0; - pid = waitpid(pid, &mut status, WNOHANG); - if pid > 0 { - print!("\n[&] + done {}", stanza); - io::stdout().flush().unwrap(); - } - } - }) - .unwrap(); - } - - Ok(0) - } - - /// Alias to [Rune::incant_none] - pub fn incant_and(verse: &Verse, out: &mut Vec<u8>) -> Result<i32, io::Error> { - Rune::incant_none(verse, out) - } - - /// Alias to [Rune::incant_none] - pub fn incant_continue(verse: &Verse, out: &mut Vec<u8>) -> Result<i32, io::Error> { - Rune::incant_none(verse, out) - } - - /// Recite a verse with [Rune::Read] - /// - /// Call this function on a [Verse] with a meter of type [Rune::Read]. - /// This reads the specified files into `out`, then makes a call to - /// [Rune::incant_none] with all the contents of `out`. Anything piped to - /// this command will appear in `out` first, and any subsequent files will - /// be appended. - /// - /// # Arguments - /// * `verse: &Verse` - The verse to recite - /// * `paths: &Verse` - The next verse (i.e. the file paths) - /// * `out: &mut String` - A string that may have output from the last command, - /// and that will be used to store the contents of the - /// file paths in `next` - pub fn incant_read( - verse: &mut Verse, - out: &mut Vec<u8>, - pids: &mut Arc<Mutex<Vec<i32>>>, - ) -> Result<i32, io::Error> { - // Split the verse from the paths - let paths = verse.split("<"); - - // Read all file specified in the next verse into 'out', since there - // may also be piped output from the last command - for path in paths.iter() { - let mut file = OpenOptions::new().read(true).open(path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - out.append(&mut contents.as_bytes().to_vec()); - } - - // Alias incant_<meter> - match verse.meter { - Rune::None => Rune::incant_none(&verse, out), - Rune::Couplet => Rune::incant_couplet(&verse, out), - Rune::Quiet => Rune::incant_quiet(&verse, out, pids), - Rune::And => Rune::incant_and(&verse, out), - Rune::Continue => Rune::incant_continue(&verse, out), - _ => unreachable!(), - } - } - - /// Recite a verse with [Rune::Write] - /// - /// Call this function on a [Verse] with a meter of type [Rune::Write]. - /// This writes the output of the verse into the specified files, after - /// making a call to [Rune::incant_couplet]. - /// - /// # Arguments - /// * `verse: &Verse` - The verse to recite - /// * `paths: &Verse` - The next verse (i.e. the file paths) - /// * `out: &mut String` - A string that may have output from the last command, - /// and that will be used to store the contents of the - /// file paths in `next` - pub fn incant_write( - verse: &mut Verse, - out: &mut Vec<u8>, - pids: &mut Arc<Mutex<Vec<i32>>>, - ) -> Result<i32, io::Error> { - // Split the verse from the paths - let mut paths = Arc::new(Mutex::new(verse.split("<"))); - - // Alias incant_<meter> - // let status = Rune::incant_couplet(&verse, out)?; - let status = match verse.meter { - Rune::None => Rune::incant_couplet(&verse, out)?, - Rune::Couplet => Rune::incant_couplet(&verse, out)?, - Rune::Quiet => Rune::incant_quiet_io(&verse, out, pids, &mut paths)?, - Rune::And => Rune::incant_couplet(&verse, out)?, - Rune::Continue => Rune::incant_couplet(&verse, out)?, - _ => unreachable!(), - }; - - // Write output to each file specified in the next verse - for path in paths.lock().unwrap().iter() { - let mut file = OpenOptions::new().create(true).write(true).open(path)?; - file.write(out)?; - } - - // Clear out - out.clear(); - - // Return the exit status - Ok(status) - } - - /// Recite a verse with [Rune::Addendum] - /// - /// Same as [Rune::Write], except it appends to the file(s) specified, - /// instead of overwriting them. - /// - /// # Arguments - /// * `verse: &Verse` - The verse to recite - /// * `paths: &Verse` - The next verse (i.e. the file paths) - /// * `out: &mut String` - A string that may have output from the last command, - /// and that will be used to store the contents of the - /// file paths in `next` - pub fn incant_addendum( - verse: &mut Verse, - out: &mut Vec<u8>, - pids: &mut Arc<Mutex<Vec<i32>>>, - ) -> Result<i32, io::Error> { - // Split the verse from the paths - let mut paths = Arc::new(Mutex::new(verse.split("<"))); - - // Alias incant_<meter> - // let status = Rune::incant_couplet(&verse, out)?; - let status = match verse.meter { - Rune::None => Rune::incant_couplet(&verse, out)?, - Rune::Couplet => Rune::incant_couplet(&verse, out)?, - Rune::Quiet => Rune::incant_quiet_io(&verse, out, pids, &mut paths)?, - Rune::And => Rune::incant_couplet(&verse, out)?, - Rune::Continue => Rune::incant_couplet(&verse, out)?, - _ => unreachable!(), - }; - - // Write output to each file specified in the next verse - for path in paths.lock().unwrap().iter() { - let mut file = OpenOptions::new().create(true).append(true).open(path)?; - file.write(out)?; - } - - // Clear out - out.clear(); - - // Return the exit status - Ok(status) - } - - /// Same as incant_quiet, except capture STDOUT into `out` - pub fn incant_quiet_io( - verse: &Verse, - out: &mut Vec<u8>, - pids: &mut Arc<Mutex<Vec<i32>>>, - paths: &mut Arc<Mutex<Vec<String>>>, - ) -> Result<i32, io::Error> { - let child = Arc::new(Mutex::new(iobtask!(verse, out))); - println!("[&] {}", child.lock().unwrap().id()); - - pids.lock().unwrap().push(child.lock().unwrap().id() as i32); - let stanza = verse.stanza.join(" ").to_string(); - let pids = Arc::clone(pids); - let paths = Arc::clone(paths); - let io = Arc::new(verse.io); - - unsafe { - signal_hook::low_level::register(signal_hook::consts::SIGCHLD, move || { - for pid in pids.lock().unwrap().iter() { - let mut pid = *pid; - let mut status: i32 = 0; - pid = waitpid(pid, &mut status, WNOHANG); - if pid > 0 { - print!("\n[&] + done {}", stanza); - let mut bytes: Vec<u8> = Vec::new(); - let mut child = child.lock().unwrap(); - child - .stdout - .as_mut() - .unwrap() - .read_to_end(&mut bytes) - .unwrap(); - for path in paths.lock().unwrap().iter() { - let file = if io == Rune::Write.into() { - OpenOptions::new().create(true).write(true).open(path) - } else if io == Rune::Addendum.into() { - OpenOptions::new().create(true).append(true).open(path) - } else { - unreachable!(); - }; - - let _ = file.unwrap().write(&bytes); - } - io::stdout().flush().unwrap(); - } - } - }) - .unwrap(); - } - - Ok(0) - } -} |