summaryrefslogtreecommitdiffstats
path: root/src/poem/elements/rune.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/poem/elements/rune.rs')
-rw-r--r--src/poem/elements/rune.rs339
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)
- }
-}