summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock57
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs20
-rw-r--r--src/recite.rs181
-rw-r--r--src/recite/erro.rs15
-rw-r--r--src/recite/ps.rs71
6 files changed, 231 insertions, 115 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5e36732..7654911 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6,7 +6,9 @@ version = 3
name = "dwarvish"
version = "0.0.0"
dependencies = [
+ "libc",
"signal-hook",
+ "thiserror",
]
[[package]]
@@ -16,6 +18,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -33,3 +53,40 @@ checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
+
+[[package]]
+name = "syn"
+version = "2.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
diff --git a/Cargo.toml b/Cargo.toml
index ae18cfc..0f5a2c3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,4 +10,6 @@ name = "dwvsh"
path = "src/main.rs"
[dependencies]
+libc = "0.2.153"
signal-hook = "0.3.17"
+thiserror = "1.0.57"
diff --git a/src/main.rs b/src/main.rs
index 986e006..44f2726 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,5 @@
mod recite;
+use libc::{waitpid, WNOHANG};
use recite::path::prefresh;
use recite::Poem;
use std::io::{self, Write};
@@ -53,11 +54,12 @@ fn repl(path: &Vec<&Path>, prompt: &str) {
// Parse a poem
let poem = Poem::read(poetry);
match poem {
- Some(poem) => {
- poem.recite(path, &mut bins);
- }
+ Some(poem) => match poem.recite(path, &mut bins) {
+ Ok(_) => {}
+ Err(e) => eprintln!("dwvsh: {}", e),
+ },
None => {}
- }
+ };
}
}
@@ -87,6 +89,16 @@ fn main() {
io::stdout().flush().unwrap();
})
.unwrap();
+
+ signal_hook::low_level::register(signal_hook::consts::SIGCHLD, move || {
+ let mut status: i32 = -1;
+ let pid: i32 = waitpid(-1, &mut status, WNOHANG);
+ if pid != -1 {
+ print!("[&] + done {}", pid);
+ io::stdout().flush().unwrap();
+ }
+ })
+ .unwrap();
};
// Begin evaluating commands
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<i32, Mishap> {
+ 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<i32, Mishap> {
+ 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<i32, Mishap> {
+ let child = task!(verse, out);
+ println!("[&] {}", child.id());
+ Ok(0)
+ }
+
+ fn incant_and(verse: &Verse, out: &mut String) -> Result<i32, Mishap> {
+ Meter::incant_none(verse, out)
+ }
+
+ fn incant_string(verse: &Verse, out: &mut String) -> Result<i32, Mishap> {
+ 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<String>) -> bool {
+ pub fn recite(&self, path: &Vec<&Path>, bins: &mut Vec<String>) -> 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
diff --git a/src/recite/erro.rs b/src/recite/erro.rs
new file mode 100644
index 0000000..00f57b3
--- /dev/null
+++ b/src/recite/erro.rs
@@ -0,0 +1,15 @@
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum Mishap {
+ #[error("broken pipe: {0}")]
+ BrokenPipe(String),
+ // #[error("exec format error: {0}")]
+ // ExecFormat(String),
+ #[error("permission denied: {0}")]
+ PermissionDenied(String),
+ #[error("terminated: {0}")]
+ Terminated(String),
+ #[error("exec error: {0}")]
+ Else(String),
+}
diff --git a/src/recite/ps.rs b/src/recite/ps.rs
new file mode 100644
index 0000000..3557505
--- /dev/null
+++ b/src/recite/ps.rs
@@ -0,0 +1,71 @@
+#[macro_export]
+macro_rules! task {
+ ($verse:expr, $out:expr) => {
+ if $verse.couplet {
+ let mut child = Command::new($verse.verb())
+ .args($verse.clause())
+ .stdin(Stdio::piped())
+ .spawn()
+ .map_err(|e| match e.kind() {
+ io::ErrorKind::PermissionDenied => Mishap::PermissionDenied($verse.verb()),
+ _ => Mishap::Else($verse.verb()),
+ })?;
+
+ let stdin = child
+ .stdin
+ .as_mut()
+ .ok_or(Mishap::BrokenPipe($verse.verb()))?;
+ stdin
+ .write_all(&$out.as_bytes())
+ .map_err(|_| Mishap::BrokenPipe($verse.verb()))?;
+ $out.clear();
+
+ child
+ } else {
+ Command::new($verse.verb())
+ .args($verse.clause())
+ .spawn()
+ .map_err(|e| match e.kind() {
+ io::ErrorKind::PermissionDenied => Mishap::PermissionDenied($verse.verb()),
+ _ => Mishap::Else($verse.verb()),
+ })?
+ }
+ };
+}
+
+#[macro_export()]
+macro_rules! ctask {
+ ($verse:expr, $out:expr) => {
+ if $verse.couplet {
+ let mut child = Command::new($verse.verb())
+ .args($verse.clause())
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn()
+ .map_err(|e| match e.kind() {
+ io::ErrorKind::PermissionDenied => Mishap::PermissionDenied($verse.verb()),
+ _ => Mishap::Else($verse.verb()),
+ })?;
+
+ let stdin = child
+ .stdin
+ .as_mut()
+ .ok_or(Mishap::BrokenPipe($verse.verb()))?;
+ stdin
+ .write_all(&$out.as_bytes())
+ .map_err(|_| Mishap::BrokenPipe($verse.verb()))?;
+ $out.clear();
+
+ child
+ } else {
+ Command::new($verse.verb())
+ .args($verse.clause())
+ .stdout(Stdio::piped())
+ .spawn()
+ .map_err(|e| match e.kind() {
+ io::ErrorKind::PermissionDenied => Mishap::PermissionDenied($verse.verb()),
+ _ => Mishap::Else($verse.verb()),
+ })?
+ }
+ };
+}