From 0548e74cb3227716cf445f27bd64b8c0b4d0f981 Mon Sep 17 00:00:00 2001 From: Rory Dudley Date: Mon, 26 Feb 2024 23:14:13 -0700 Subject: Cleanup recite(), custom errors, fixed forking First off, moved the giant match statements out of recite(), and into macros in src/recite/ps.rs. There still needs to be two, since any verse using the 'couplet' meter will need to redirect its STDOUT. Now the recite() function returns a Result<(), Mishap>, which can be invoked when calling the incant_ functions. Custom errors were added in the form of 'Mishap''s. They are intended to be returned from the incant_ functions, in the event that something goes wrong with the Command::spawn() or Child::wait(). They each take a String, which should be the verb or stanza that was entered by the user. The incant_ functions separate the functionality of each type of meter from the recite() function. They return a Result, where i32 is the exit code of the program that ran, and Mishap is a possible error. Before, the shell was cheating at forking a process to the background. It would actually spawn a thread to wait for that process to finish. Now, the program simply registers a handler for SIGCHLD, and uses libc's waitpid() function to reap the child process, and print some output to the user, indicating that it's finished. --- src/recite.rs | 181 +++++++++++++++++++++++----------------------------------- 1 file changed, 70 insertions(+), 111 deletions(-) (limited to 'src/recite.rs') diff --git a/src/recite.rs b/src/recite.rs index 4e1dc1f..bfcd6b1 100644 --- a/src/recite.rs +++ b/src/recite.rs @@ -1,5 +1,10 @@ +pub mod erro; pub mod path; +mod ps; +use crate::ctask; +use crate::task; use core::fmt; +use erro::Mishap; use path::prefresh; use std::io::{self, Write}; use std::path::Path; @@ -44,6 +49,56 @@ impl fmt::Display for Meter { } } +impl Meter { + fn incant_none(verse: &Verse, out: &mut String) -> Result { + let child = task!(verse, out); + + let output = child + .wait_with_output() + .map_err(|_| Mishap::Terminated(verse.stanza.to_string()))?; + + if !output.status.success() { + return Err(Mishap::Else(verse.stanza.to_string())); + } + + Ok(output.status.code().unwrap_or(0)) + } + + fn incant_couplet(verse: &Verse, out: &mut String) -> Result { + let child = ctask!(verse, out); + + let output = child + .wait_with_output() + .map_err(|_| Mishap::Terminated(verse.stanza.to_string()))?; + + if !output.status.success() { + return Err(Mishap::Else(verse.stanza.to_string())); + } + + out.push_str( + String::from_utf8_lossy(&output.stdout) + .into_owned() + .as_str(), + ); + + Ok(output.status.code().unwrap_or(0)) + } + + fn incant_quiet(verse: &Verse, out: &mut String) -> Result { + let child = task!(verse, out); + println!("[&] {}", child.id()); + Ok(0) + } + + fn incant_and(verse: &Verse, out: &mut String) -> Result { + Meter::incant_none(verse, out) + } + + fn incant_string(verse: &Verse, out: &mut String) -> Result { + Meter::incant_none(verse, out) + } +} + /// Holds a program to be called /// /// This is simply the first word in a full command [String], dilineated via @@ -259,7 +314,7 @@ impl Poem { /// None => {} /// } /// ``` - pub fn recite(&self, path: &Vec<&Path>, bins: &mut Vec) -> bool { + pub fn recite(&self, path: &Vec<&Path>, bins: &mut Vec) -> Result<(), Mishap> { // Variable for storing the output of a piped verse let mut out: String = String::new(); @@ -300,121 +355,25 @@ impl Poem { } } - // If the verse is a couplet, it means it needs the output of the - // previous verse on `STDIN` - if verse.couplet { - match verse.meter { - Meter::Couplet => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("dwvsh: error 0"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 6"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 7"); - out.clear(); - - let output = child.wait_with_output().unwrap(); - out = String::from_utf8_lossy(&output.stdout).to_string(); + // Incant the verse, based on its meter + match verse.meter { + Meter::None => Meter::incant_none(verse, &mut out)?, + Meter::Couplet => Meter::incant_couplet(verse, &mut out)?, + Meter::Quiet => Meter::incant_quiet(verse, &mut out)?, + Meter::And => Meter::incant_and(verse, &mut out)?, + Meter::String => match Meter::incant_string(verse, &mut out) { + Ok(_) => 0, + Err(e) => { + eprintln!("dwvsh: {}", e); + 1 } - Meter::Quiet => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .stdin(Stdio::piped()) - .spawn() - .expect("dwvsh: error 1"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 8"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 9"); - out.clear(); - - print!("[f] {}", child.id()); - std::thread::spawn(move || { - child.wait().unwrap(); - println!("[f] +done {}", child.id()); - io::stdout().flush().unwrap(); - }); - } - Meter::String => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 5"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 8"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 9"); - out.clear(); - - child.wait().unwrap(); - } - Meter::And | Meter::None => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .stdin(Stdio::piped()) - .spawn() - .expect("dwvsh: error 2"); - - let stdin = child.stdin.as_mut().expect("dwvsh: error 10"); - stdin.write_all(&out.as_bytes()).expect("dwvsh: error 11"); - out.clear(); - - if !child.wait().unwrap().success() { - break; - } - } - }; - } else { - match verse.meter { - Meter::Couplet => { - let child = Command::new(verse.verb()) - .args(verse.clause()) - .stdout(Stdio::piped()) - .spawn() - .expect("dwvsh: error 3"); - - let output = child.wait_with_output().unwrap(); - out = String::from_utf8_lossy(&output.stdout).to_string(); - } - Meter::Quiet => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 4"); - - println!("[f] {}", child.id()); - std::thread::spawn(move || { - child.wait().unwrap(); - print!("[f] +done {}\n", child.id()); - io::stdout().flush().unwrap(); - }); - } - Meter::String => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 5"); - - child.wait().unwrap(); - } - Meter::And | Meter::None => { - let mut child = Command::new(verse.verb()) - .args(verse.clause()) - .spawn() - .expect("dwvsh: error 5"); - - if !child.wait().unwrap().success() { - break; - } - } - }; - } + }, + }; } // If we've successfully exited the loop, then all verse's were // properly recited - true + Ok(()) } /// Parse a [Poem] from a raw [String] input -- cgit v1.2.3