diff options
author | Rory Dudley | 2024-06-04 16:25:32 -0600 |
---|---|---|
committer | Rory Dudley | 2024-06-04 16:25:32 -0600 |
commit | c4cd1e2c165c4f34ebf67fa9350f8732b2aeca13 (patch) | |
tree | a8dd06c7563c205eb4710f620cf89d89ae17d98b /src/poem/anthology | |
parent | dedadcfd30516c40692fe495a6ad10aea7c050de (diff) | |
download | dwarvish-c4cd1e2c165c4f34ebf67fa9350f8732b2aeca13.tar.gz |
Updated the way built-in commands are called/used
Previously, built-in commands were fairly primitive, merely outputting
STDOUT and STDERR with the print! macros. However, we need them to
behave like normal programs, that is:
- Acknowledge their verse's meter (forking, piping, etc.),
- Ability to capture STDOUT and STDERR (>, 2>),
- and Affect the currently running environment.
For these reasons, the anthology was reworked, and now contains the
Anthology struct, which mimics both std::process::{Child, Command}.
The AnthologyStdin helper struct was also created, for built-ins to
take input on STDIN, though no built-in is currently using it.
Each built-ins' incant functions were updated to return a
std::process::Output. It contains output from STDOUT, output from
STDERR, and the exit code of the "process".
A fix was also implemented for aliases, where the STDOUT and STDERR
vectors were not being copied to the newly constructed verse.
Notes
Notes:
There is some cleanup that needs to happen on this patch. For one, the
spellcheck function is no longer being used, so there is a generic OS
error if the program cannot be found in the $PATH. Also,
anthology::lookup gets called twice, which shouldn't need to happen.
Diffstat (limited to 'src/poem/anthology')
-rw-r--r-- | src/poem/anthology/alias.rs | 56 | ||||
-rw-r--r-- | src/poem/anthology/cd.rs | 53 | ||||
-rw-r--r-- | src/poem/anthology/exit.rs | 3 | ||||
-rw-r--r-- | src/poem/anthology/export.rs | 47 | ||||
-rw-r--r-- | src/poem/anthology/source.rs | 98 | ||||
-rw-r--r-- | src/poem/anthology/which.rs | 44 |
6 files changed, 238 insertions, 63 deletions
diff --git a/src/poem/anthology/alias.rs b/src/poem/anthology/alias.rs index 45ee7f2..0746b59 100644 --- a/src/poem/anthology/alias.rs +++ b/src/poem/anthology/alias.rs @@ -1,6 +1,7 @@ -use crate::poem::Verse; use std::collections::BTreeMap; use std::collections::HashMap; +use std::os::unix::process::ExitStatusExt; +use std::process::{ExitStatus, Output}; /// alias /// @@ -11,8 +12,15 @@ use std::collections::HashMap; /// ```sh /// alias vim=nvim /// ``` -pub fn incant(verse: &Verse, out: &mut Vec<u8>, aliases: &mut HashMap<String, String>) -> i32 { - match verse.clause() { +pub fn incant( + clause: &Option<Vec<String>>, + uout: bool, + aliases: &mut HashMap<String, String>, +) -> Output { + let status = 0; + let mut out: Vec<u8> = Vec::new(); + let err: Vec<u8> = Vec::new(); + match clause { Some(clause) => { for stanza in clause { let (key, val) = match stanza.split_once("=") { @@ -24,7 +32,7 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, aliases: &mut HashMap<String, St } None => { let mut lines = Vec::new(); - let sorted: BTreeMap<_, _> = aliases.into_iter().collect(); + let sorted: BTreeMap<_, _> = aliases.iter().collect(); for (key, val) in sorted { let line = if key.contains(' ') && val.contains(' ') { format!("'{}'='{}'", key, val) @@ -40,14 +48,19 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, aliases: &mut HashMap<String, St lines.push(line); } - if verse.couplet > 0 { - *out = format!("{}\n", lines.join("\n")).as_bytes().to_vec(); + if uout { + out.append(&mut format!("{}\n", lines.join("\n")).as_bytes().to_vec()); } else { println!("{}", lines.join("\n")); } } } - 0 + + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + } } /// unalias @@ -59,17 +72,34 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, aliases: &mut HashMap<String, St /// ```sh /// unalias vim /// ``` -pub fn unincant(verse: &Verse, aliases: &mut HashMap<String, String>) -> i32 { - match verse.clause() { +pub fn unincant( + clause: &Option<Vec<String>>, + uerr: bool, + aliases: &mut HashMap<String, String>, +) -> Output { + let out: Vec<u8> = Vec::new(); + let mut err: Vec<u8> = Vec::new(); + + let status = match clause { Some(clause) => { for stanza in clause { - aliases.remove(&stanza); + aliases.remove(stanza); } + 0 } None => { - eprintln!("unalias: not enough arguments"); - return 1; + if uerr { + err.append(&mut "unalias: not enough arguments".as_bytes().to_vec()); + } else { + eprintln!("unalias: not enough arguments"); + } + 1 } + }; + + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, } - 0 } diff --git a/src/poem/anthology/cd.rs b/src/poem/anthology/cd.rs index 5b39359..bdf04f6 100644 --- a/src/poem/anthology/cd.rs +++ b/src/poem/anthology/cd.rs @@ -1,5 +1,6 @@ -use crate::poem::Verse; use std::env; +use std::os::unix::process::ExitStatusExt; +use std::process::{ExitStatus, Output}; /// cd /// @@ -12,27 +13,57 @@ use std::env; /// ```sh /// cd ~/.config # Change into /home/<user>/.config /// ``` -pub fn incant(verse: &Verse) -> i32 { - let path = match verse.clause() { +pub fn incant(clause: &Option<Vec<String>>, uerr: bool) -> Output { + let status; + let out: Vec<u8> = Vec::new(); + let mut err: Vec<u8> = Vec::new(); + let path = match clause { Some(path) => path[0].to_string(), None => match env::var("HOME") { Ok(val) => val, Err(_) => { - eprintln!("cd: unknown home, staying in pwd"); - return 1; + status = 1; + if uerr { + err.append(&mut "cd: unknown home, staying in pwd\n".as_bytes().to_vec()); + } else { + eprintln!("cd: unknown home, staying in pwd"); + } + return Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + }; } }, }; - match std::env::set_current_dir(&path) { + status = match std::env::set_current_dir(&path) { Ok(_) => 0, Err(e) => { - eprintln!( - "cd: unable to change into {}: {}", - path, - e.to_string().to_lowercase() - ); + if uerr { + err.append( + &mut format!( + "cd: unable to change into {}: {}\n", + path, + e.to_string().to_lowercase() + ) + .as_bytes() + .to_vec(), + ); + } else { + eprintln!( + "cd: unable to change into {}: {}", + path, + e.to_string().to_lowercase() + ); + } 1 } + }; + + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, } } diff --git a/src/poem/anthology/exit.rs b/src/poem/anthology/exit.rs index 46fe13c..6bbaa33 100644 --- a/src/poem/anthology/exit.rs +++ b/src/poem/anthology/exit.rs @@ -1,4 +1,5 @@ use std::process::exit; +use std::process::Output; /// exit /// @@ -7,6 +8,6 @@ use std::process::exit; /// # Aliases /// * exit /// * quit -pub fn incant() -> i32 { +pub fn incant() -> Output { exit(0); } diff --git a/src/poem/anthology/export.rs b/src/poem/anthology/export.rs index aa5a894..35a24ae 100644 --- a/src/poem/anthology/export.rs +++ b/src/poem/anthology/export.rs @@ -1,6 +1,7 @@ -use crate::poem::Verse; use std::collections::BTreeMap; use std::env; +use std::os::unix::process::ExitStatusExt; +use std::process::{ExitStatus, Output}; /// export /// @@ -15,8 +16,11 @@ use std::env; /// ```sh /// export FOO=BAR /// ``` -pub fn incant(verse: &Verse) -> i32 { - match verse.clause() { +pub fn incant(clause: &Option<Vec<String>>, uout: bool) -> Output { + let status = 0; + let mut out: Vec<u8> = Vec::new(); + let err: Vec<u8> = Vec::new(); + match clause { Some(clause) => { for stanza in clause { let (key, val) = match stanza.split_once("=") { @@ -27,14 +31,25 @@ pub fn incant(verse: &Verse) -> i32 { } } None => { + let mut lines = Vec::new(); let sorted: BTreeMap<_, _> = env::vars().into_iter().collect(); for (key, val) in sorted { - println!("{}={}", key, val); + lines.push(format!("{}={}", key, val)); + } + + if uout { + out.append(&mut format!("{}\n", lines.join("\n")).as_bytes().to_vec()); + } else { + println!("{}", lines.join("\n")); } } } - 0 + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + } } /// unset @@ -47,17 +62,29 @@ pub fn incant(verse: &Verse) -> i32 { /// ```sh /// unset FOO /// ``` -pub fn unincant(verse: &Verse) -> i32 { - match verse.clause() { +pub fn unincant(clause: &Option<Vec<String>>, uerr: bool) -> Output { + let mut status = 0; + let out: Vec<u8> = Vec::new(); + let mut err: Vec<u8> = Vec::new(); + match clause { Some(clause) => { for stanza in clause { env::remove_var(stanza); } } None => { - eprintln!("unset: not enough arguments"); - return 1; + status = 1; + if uerr { + err.append(&mut "unset: not enough arguments\n".as_bytes().to_vec()); + } else { + eprintln!("unset: not enough arguments"); + } } } - 0 + + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + } } diff --git a/src/poem/anthology/source.rs b/src/poem/anthology/source.rs index 3c81110..0ed759c 100644 --- a/src/poem/anthology/source.rs +++ b/src/poem/anthology/source.rs @@ -1,7 +1,8 @@ use crate::compose::Environment; -use crate::poem::Verse; use crate::poem::{read::Readable, recite::Reciteable, Poem}; use std::fs; +use std::os::unix::process::ExitStatusExt; +use std::process::{ExitStatus, Output}; /// source /// @@ -12,12 +13,29 @@ use std::fs; /// ```sh /// source ~/.dwvshrc /// ``` -pub fn incant(verse: &Verse, out: &mut Vec<u8>, env: &mut Environment) -> i32 { - let files = match verse.clause() { +pub fn incant( + clause: &Option<Vec<String>>, + uout: bool, + uerr: bool, + env: &mut Environment, +) -> Output { + let mut status = 0; + let mut out: Vec<u8> = Vec::new(); + let mut err: Vec<u8> = Vec::new(); + let files = match clause { Some(clause) => clause, None => { - eprintln!("source: not enough arguments"); - return 1; + status = 1; + if uerr { + err.append(&mut "source: not enough arguments\n".as_bytes().to_vec()); + } else { + eprintln!("source: not enough arguments"); + } + return Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + }; } }; @@ -25,31 +43,77 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, env: &mut Environment) -> i32 { let poetry = match fs::read_to_string(&file) { Ok(poetry) => poetry, Err(e) => { - eprintln!( - "source: could not load {}: {}", - file, - e.to_string().to_lowercase() - ); - return 127; + status = 127; + if uerr { + err.append( + &mut format!( + "source: could not load {}: {}\n", + file, + e.to_string().to_lowercase() + ) + .as_bytes() + .to_vec(), + ); + } else { + eprintln!( + "source: could not load {}: {}", + file, + e.to_string().to_lowercase() + ); + } + return Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + }; } }; let poem = match Poem::read(poetry, env) { Ok(poem) => poem, Err(e) => { - eprintln!("dwvsh: {}", e.to_string().to_lowercase()); + if uerr { + err.append( + &mut format!("dwvsh: {}", e.to_string().to_lowercase()) + .as_bytes() + .to_vec(), + ); + } else { + eprintln!("dwvsh: {}", e.to_string().to_lowercase()); + } continue; } }; - *out = match poem.recite(env) { - Ok(out) => out, + status = match poem.recite(env) { + Ok(mut sout) => { + if uout { + out.append(&mut sout); + } else { + if !sout.is_empty() { + println!("{}", String::from_utf8_lossy(&sout)); + } + } + 0 + } Err(e) => { - eprintln!("dwvsh: {}", e.to_string().to_lowercase()); - continue; + if uerr { + err.append( + &mut format!("dwvsh: {}", e.to_string().to_lowercase()) + .as_bytes() + .to_vec(), + ); + } else { + eprintln!("dwvsh: {}", e.to_string().to_lowercase()); + } + 1 } }; } - 0 + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + } } diff --git a/src/poem/anthology/which.rs b/src/poem/anthology/which.rs index 515b52e..6fe709c 100644 --- a/src/poem/anthology/which.rs +++ b/src/poem/anthology/which.rs @@ -1,16 +1,20 @@ use crate::compose::Environment; use crate::poem::Verse; +use std::os::unix::process::ExitStatusExt; +use std::process::{ExitStatus, Output}; -pub fn incant(verse: &Verse, out: &mut Vec<u8>, env: &Environment) -> i32 { +pub fn incant(clause: &Option<Vec<String>>, uout: bool, uerr: bool, env: &Environment) -> Output { let mut status = 0; - match verse.clause() { + let mut out: Vec<u8> = Vec::new(); + let mut err: Vec<u8> = Vec::new(); + match clause { Some(clause) => { let mut output: String; for word in clause { // Check if it's an alias - if env.aliases.contains_key(&word) { - output = format!("{}: aliased to {}\n", word, env.aliases.get(&word).unwrap()); - if verse.couplet > 0 { + if env.aliases.contains_key(word) { + output = format!("{}: aliased to {}\n", word, env.aliases.get(word).unwrap()); + if uout { out.append(&mut output.as_bytes().to_vec()); } else { print!("{}", output); @@ -22,7 +26,7 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, env: &Environment) -> i32 { match super::lookup(&word) { Some(_) => { output = format!("{}: shell built-in command\n", word); - if verse.couplet > 0 { + if uout { out.append(&mut output.as_bytes().to_vec()); } else { print!("{}", output); @@ -38,7 +42,7 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, env: &Environment) -> i32 { match verb.spellcheck(&env.bins) { Some(i) => { output = format!("{}\n", env.bins[i]); - if verse.couplet > 0 { + if uout { out.append(&mut output.as_bytes().to_vec()); } else { print!("{}", output); @@ -47,15 +51,33 @@ pub fn incant(verse: &Verse, out: &mut Vec<u8>, env: &Environment) -> i32 { None => { output = format!("{} not found\n", word); status = 1; - eprint!("{}", output); + if uerr { + err.append(&mut output.as_bytes().to_vec()); + } else { + eprint!("{}", output); + } } } } } None => { - eprintln!("which: not enough arguments"); - return 1; + status = 1; + if uerr { + err.append(&mut "which: not enough arguments".as_bytes().to_vec()); + } else { + eprintln!("which: not enough arguments"); + } + return Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + }; } } - status + + Output { + status: ExitStatus::from_raw(status), + stdout: out, + stderr: err, + } } |