Implemented flags and extra safety for zoltraak
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use std::{os::unix::fs::OpenOptionsExt, sync::LazyLock};
|
use std::{os::unix::fs::OpenOptionsExt, sync::LazyLock};
|
||||||
|
|
||||||
use crate::{getopt::{get_opts_from_tokens, Opt, OptSet}, jobs::JobBldr, libsh::error::{Note, ShErr, ShErrKind, ShResult, ShResultExt}, parse::{NdRule, Node}, prelude::*, procio::IoStack};
|
use crate::{getopt::{get_opts_from_tokens, Opt, OptSet}, jobs::JobBldr, libsh::error::{Note, ShErr, ShErrKind, ShResult, ShResultExt}, parse::{NdRule, Node}, prelude::*, procio::{borrow_fd, IoStack}};
|
||||||
|
|
||||||
use super::setup_builtin;
|
use super::setup_builtin;
|
||||||
|
|
||||||
@@ -15,28 +15,93 @@ pub const ZOLTRAAK_OPTS: LazyLock<OptSet> = LazyLock::new(|| {
|
|||||||
].into()
|
].into()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
||||||
|
struct ZoltFlags: u32 {
|
||||||
|
const DRY = 0b000001;
|
||||||
|
const CONFIRM = 0b000010;
|
||||||
|
const NO_PRESERVE_ROOT = 0b000100;
|
||||||
|
const RECURSIVE = 0b001000;
|
||||||
|
const FORCE = 0b010000;
|
||||||
|
const VERBOSE = 0b100000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Annihilate a file
|
/// Annihilate a file
|
||||||
///
|
///
|
||||||
/// This command works similarly to 'rm', but behaves more destructively.
|
/// This command works similarly to 'rm', but behaves more destructively.
|
||||||
/// The file given as an argument is completely destroyed. The command works by shredding all of the data contained in the file, before truncating the length of the file to 0 to ensure that not even any metadata remains.
|
/// The file given as an argument is completely destroyed. The command works by shredding all of the data contained in the file, before truncating the length of the file to 0 to ensure that not even any metadata remains.
|
||||||
pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
|
pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
|
||||||
let NdRule::Command { assignments, argv } = node.class else {
|
let NdRule::Command { assignments: _, argv } = node.class else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
let mut flags = ZoltFlags::empty();
|
||||||
|
|
||||||
let (argv,opts) = get_opts_from_tokens(argv);
|
let (argv,opts) = get_opts_from_tokens(argv);
|
||||||
|
|
||||||
|
for opt in opts {
|
||||||
|
if !ZOLTRAAK_OPTS.contains(&opt) {
|
||||||
|
return Err(
|
||||||
|
ShErr::simple(
|
||||||
|
ShErrKind::SyntaxErr,
|
||||||
|
format!("zoltraak: unrecognized option '{opt}'")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
match opt {
|
||||||
|
Opt::Long(flag) => {
|
||||||
|
match flag.as_str() {
|
||||||
|
"no-preserve-root" => flags |= ZoltFlags::NO_PRESERVE_ROOT,
|
||||||
|
"confirm" => flags |= ZoltFlags::CONFIRM,
|
||||||
|
"dry-run" => flags |= ZoltFlags::DRY,
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opt::Short(flag) => {
|
||||||
|
match flag {
|
||||||
|
'r' => flags |= ZoltFlags::RECURSIVE,
|
||||||
|
'f' => flags |= ZoltFlags::FORCE,
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (argv, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
let (argv, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||||
|
|
||||||
|
let mut io_frame = io_frame.unwrap();
|
||||||
|
io_frame.redirect()?;
|
||||||
|
|
||||||
for (arg,span) in argv {
|
for (arg,span) in argv {
|
||||||
annihilate(&arg, false).blame(span)?;
|
if &arg == "/" && !flags.contains(ZoltFlags::NO_PRESERVE_ROOT) {
|
||||||
|
return Err(
|
||||||
|
ShErr::simple(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
"zoltraak: Attempted to destroy root directory '/'"
|
||||||
|
)
|
||||||
|
.with_note(
|
||||||
|
Note::new("If you really want to do this, you can use the --no-preserve-root flag")
|
||||||
|
.with_sub_notes(vec![
|
||||||
|
"Example: 'zoltraak --no-preserve-root /'"
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if let Err(e) = annihilate(&arg, flags).blame(span) {
|
||||||
|
io_frame.restore()?;
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io_frame.restore()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn annihilate(path: &str, allow_dirs: bool) -> ShResult<()> {
|
fn annihilate(path: &str, flags: ZoltFlags) -> ShResult<()> {
|
||||||
let path_buf = PathBuf::from(path);
|
let path_buf = PathBuf::from(path);
|
||||||
|
let is_recursive = flags.contains(ZoltFlags::RECURSIVE);
|
||||||
|
let is_verbose = flags.contains(ZoltFlags::VERBOSE);
|
||||||
|
|
||||||
const BLOCK_SIZE: u64 = 4096;
|
const BLOCK_SIZE: u64 = 4096;
|
||||||
|
|
||||||
@@ -74,10 +139,14 @@ fn annihilate(path: &str, allow_dirs: bool) -> ShResult<()> {
|
|||||||
file.set_len(0)?;
|
file.set_len(0)?;
|
||||||
mem::drop(file);
|
mem::drop(file);
|
||||||
fs::remove_file(path)?;
|
fs::remove_file(path)?;
|
||||||
|
if is_verbose {
|
||||||
|
let stderr = borrow_fd(STDERR_FILENO);
|
||||||
|
write(stderr, format!("removed file '{path}'").as_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
} else if path_buf.is_dir() {
|
} else if path_buf.is_dir() {
|
||||||
if allow_dirs {
|
if is_recursive {
|
||||||
annihilate_recursive(path)?; // scary
|
annihilate_recursive(path, flags)?; // scary
|
||||||
} else {
|
} else {
|
||||||
return Err(
|
return Err(
|
||||||
ShErr::simple(
|
ShErr::simple(
|
||||||
@@ -97,19 +166,24 @@ fn annihilate(path: &str, allow_dirs: bool) -> ShResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn annihilate_recursive(dir: &str) -> ShResult<()> {
|
fn annihilate_recursive(dir: &str, flags: ZoltFlags) -> ShResult<()> {
|
||||||
let dir_path = PathBuf::from(dir);
|
let dir_path = PathBuf::from(dir);
|
||||||
|
let is_verbose = flags.contains(ZoltFlags::VERBOSE);
|
||||||
|
|
||||||
for dir_entry in fs::read_dir(&dir_path)? {
|
for dir_entry in fs::read_dir(&dir_path)? {
|
||||||
let entry = dir_entry?.path();
|
let entry = dir_entry?.path();
|
||||||
let file = entry.to_str().unwrap();
|
let file = entry.to_str().unwrap();
|
||||||
|
|
||||||
if entry.is_file() {
|
if entry.is_file() {
|
||||||
annihilate(file, true)?;
|
annihilate(file, flags)?;
|
||||||
} else if entry.is_dir() {
|
} else if entry.is_dir() {
|
||||||
annihilate_recursive(file)?;
|
annihilate_recursive(file, flags)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs::remove_dir(dir)?;
|
fs::remove_dir(dir)?;
|
||||||
|
if is_verbose {
|
||||||
|
let stderr = borrow_fd(STDERR_FILENO);
|
||||||
|
write(stderr, format!("removed directory '{dir}'").as_bytes())?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::{exec_input, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{lex::{is_field_sep, is_hard_sep, LexFlags, LexStream, Span, Tk, TkFlags, TkRule}, Redir, RedirType}, prelude::*, procio::{IoBuf, IoFrame, IoMode}, state::{read_logic, read_vars, write_meta}};
|
use crate::{exec_input, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{lex::{is_field_sep, is_hard_sep, LexFlags, LexStream, Span, Tk, TkFlags, TkRule}, Redir, RedirType}, prelude::*, procio::{IoBuf, IoFrame, IoMode}, state::{read_logic, read_vars, write_meta, LogTab}};
|
||||||
|
|
||||||
/// Variable substitution marker
|
/// Variable substitution marker
|
||||||
pub const VAR_SUB: char = '\u{fdd0}';
|
pub const VAR_SUB: char = '\u{fdd0}';
|
||||||
@@ -602,7 +602,7 @@ pub fn expand_prompt(raw: &str) -> ShResult<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Expand aliases in the given input string
|
/// Expand aliases in the given input string
|
||||||
pub fn expand_aliases(input: String, mut already_expanded: HashSet<String>) -> String {
|
pub fn expand_aliases(input: String, mut already_expanded: HashSet<String>, log_tab: &LogTab) -> String {
|
||||||
let mut result = input.clone();
|
let mut result = input.clone();
|
||||||
let tokens: Vec<_> = LexStream::new(Arc::new(input), LexFlags::empty()).collect();
|
let tokens: Vec<_> = LexStream::new(Arc::new(input), LexFlags::empty()).collect();
|
||||||
let mut expanded_this_iter: Vec<String> = vec![];
|
let mut expanded_this_iter: Vec<String> = vec![];
|
||||||
@@ -611,12 +611,13 @@ pub fn expand_aliases(input: String, mut already_expanded: HashSet<String>) -> S
|
|||||||
let Ok(tk) = token_result else { continue };
|
let Ok(tk) = token_result else { continue };
|
||||||
|
|
||||||
if !tk.flags.contains(TkFlags::IS_CMD) { continue }
|
if !tk.flags.contains(TkFlags::IS_CMD) { continue }
|
||||||
|
if tk.flags.contains(TkFlags::KEYWORD) { continue }
|
||||||
|
|
||||||
let raw_tk = tk.span.as_str().to_string();
|
let raw_tk = tk.span.as_str().to_string();
|
||||||
|
|
||||||
if already_expanded.contains(&raw_tk) { continue }
|
if already_expanded.contains(&raw_tk) { continue }
|
||||||
|
|
||||||
if let Some(alias) = read_logic(|l| l.get_alias(&raw_tk)) {
|
if let Some(alias) = log_tab.get_alias(&raw_tk) {
|
||||||
result.replace_range(tk.span.range(), &alias);
|
result.replace_range(tk.span.range(), &alias);
|
||||||
expanded_this_iter.push(raw_tk);
|
expanded_this_iter.push(raw_tk);
|
||||||
}
|
}
|
||||||
@@ -626,6 +627,6 @@ pub fn expand_aliases(input: String, mut already_expanded: HashSet<String>) -> S
|
|||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
already_expanded.extend(expanded_this_iter.into_iter());
|
already_expanded.extend(expanded_this_iter.into_iter());
|
||||||
return expand_aliases(result, already_expanded)
|
return expand_aliases(result, already_expanded, log_tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/fern.rs
11
src/fern.rs
@@ -8,18 +8,18 @@ pub mod state;
|
|||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod jobs;
|
pub mod jobs;
|
||||||
pub mod signal;
|
pub mod signal;
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests;
|
|
||||||
pub mod getopt;
|
pub mod getopt;
|
||||||
pub mod shopt;
|
pub mod shopt;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use expand::expand_aliases;
|
use crate::expand::expand_aliases;
|
||||||
use libsh::error::ShResult;
|
use libsh::error::ShResult;
|
||||||
use parse::{execute::Dispatcher, ParsedSrc};
|
use parse::{execute::Dispatcher, ParsedSrc};
|
||||||
use signal::sig_setup;
|
use signal::sig_setup;
|
||||||
use state::{source_rc, write_meta};
|
use state::{read_logic, source_rc, write_meta};
|
||||||
use termios::{LocalFlags, Termios};
|
use termios::{LocalFlags, Termios};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@@ -74,7 +74,8 @@ fn set_termios() {
|
|||||||
|
|
||||||
pub fn exec_input(input: String) -> ShResult<()> {
|
pub fn exec_input(input: String) -> ShResult<()> {
|
||||||
write_meta(|m| m.start_timer());
|
write_meta(|m| m.start_timer());
|
||||||
let input = expand_aliases(input, HashSet::new());
|
let log_tab = read_logic(|l| l.clone());
|
||||||
|
let input = expand_aliases(input, HashSet::new(), &log_tab);
|
||||||
let mut parser = ParsedSrc::new(Arc::new(input));
|
let mut parser = ParsedSrc::new(Arc::new(input));
|
||||||
parser.parse_src()?;
|
parser.parse_src()?;
|
||||||
|
|
||||||
|
|||||||
@@ -608,6 +608,7 @@ impl ParseStream {
|
|||||||
}
|
}
|
||||||
let case_pat_tk = self.next_tk().unwrap();
|
let case_pat_tk = self.next_tk().unwrap();
|
||||||
node_tks.push(case_pat_tk.clone());
|
node_tks.push(case_pat_tk.clone());
|
||||||
|
self.catch_separator(&mut node_tks);
|
||||||
|
|
||||||
let mut nodes = vec![];
|
let mut nodes = vec![];
|
||||||
while let Some(node) = self.parse_block(true /* check_pipelines */)? {
|
while let Some(node) = self.parse_block(true /* check_pipelines */)? {
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ impl Highlighter for FernReadline {
|
|||||||
|
|
||||||
impl Validator for FernReadline {
|
impl Validator for FernReadline {
|
||||||
fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||||
return Ok(ValidationResult::Valid(None));
|
|
||||||
let mut tokens = vec![];
|
let mut tokens = vec![];
|
||||||
let tk_stream = LexStream::new(Arc::new(ctx.input().to_string()), LexFlags::empty());
|
let tk_stream = LexStream::new(Arc::new(ctx.input().to_string()), LexFlags::empty());
|
||||||
for tk in tk_stream {
|
for tk in tk_stream {
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ impl LogTab {
|
|||||||
pub fn get_alias(&self, name: &str) -> Option<String> {
|
pub fn get_alias(&self, name: &str) -> Option<String> {
|
||||||
self.aliases.get(name).cloned()
|
self.aliases.get(name).cloned()
|
||||||
}
|
}
|
||||||
|
pub fn clear_aliases(&mut self) {
|
||||||
|
self.aliases.clear()
|
||||||
|
}
|
||||||
|
pub fn clear_functions(&mut self) {
|
||||||
|
self.functions.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use libsh::error::{ShErr, ShErrKind};
|
use super::*;
|
||||||
|
|
||||||
use super::super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cmd_not_found() {
|
fn cmd_not_found() {
|
||||||
@@ -95,3 +93,30 @@ fn case_no_in() {
|
|||||||
let err_fmt = format!("{e}");
|
let err_fmt = format!("{e}");
|
||||||
insta::assert_snapshot!(err_fmt)
|
insta::assert_snapshot!(err_fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_with_notes() {
|
||||||
|
let err = ShErr::simple(ShErrKind::ExecFail, "Execution failed")
|
||||||
|
.with_note(Note::new("Execution failed for this reason"))
|
||||||
|
.with_note(Note::new("Here is how to fix it: blah blah blah"));
|
||||||
|
|
||||||
|
let err_fmt = format!("{err}");
|
||||||
|
insta::assert_snapshot!(err_fmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_with_notes_and_sub_notes() {
|
||||||
|
let err = ShErr::simple(ShErrKind::ExecFail, "Execution failed")
|
||||||
|
.with_note(Note::new("Execution failed for this reason"))
|
||||||
|
.with_note(
|
||||||
|
Note::new("Here is how to fix it:")
|
||||||
|
.with_sub_notes(vec![
|
||||||
|
"blah",
|
||||||
|
"blah",
|
||||||
|
"blah"
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
let err_fmt = format!("{err}");
|
||||||
|
insta::assert_snapshot!(err_fmt)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use expand::{expand_aliases, unescape_str};
|
use super::*;
|
||||||
use parse::lex::{Tk, TkFlags, TkRule};
|
|
||||||
use state::{write_logic, write_vars};
|
|
||||||
use super::super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_expansion() {
|
fn simple_expansion() {
|
||||||
@@ -32,61 +29,97 @@ fn unescape_string() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expand_alias_simple() {
|
fn expand_alias_simple() {
|
||||||
write_logic(|l| l.insert_alias("foo", "echo foo"));
|
write_logic(|l| {
|
||||||
|
l.insert_alias("foo", "echo foo");
|
||||||
|
let input = String::from("foo");
|
||||||
|
|
||||||
let input = String::from("foo");
|
let result = expand_aliases(input, HashSet::new(), &l);
|
||||||
|
assert_eq!(result.as_str(),"echo foo");
|
||||||
let result = expand_aliases(input, HashSet::new());
|
l.clear_aliases();
|
||||||
assert_eq!(result.as_str(),"echo foo")
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expand_alias_in_if() {
|
fn expand_alias_in_if() {
|
||||||
write_logic(|l| l.insert_alias("foo", "echo foo"));
|
write_logic(|l| {
|
||||||
|
l.insert_alias("foo", "echo foo");
|
||||||
|
let input = String::from("if foo; then echo bar; fi");
|
||||||
|
|
||||||
let input = String::from("if foo; then echo bar; fi");
|
let result = expand_aliases(input, HashSet::new(), &l);
|
||||||
|
assert_eq!(result.as_str(),"if echo foo; then echo bar; fi");
|
||||||
|
l.clear_aliases();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let result = expand_aliases(input, HashSet::new());
|
#[test]
|
||||||
assert_eq!(result.as_str(),"if echo foo; then echo bar; fi")
|
fn expand_alias_multiline() {
|
||||||
|
write_logic(|l| {
|
||||||
|
l.insert_alias("foo", "echo foo");
|
||||||
|
l.insert_alias("bar", "echo bar");
|
||||||
|
let input = String::from("
|
||||||
|
foo
|
||||||
|
if true; then
|
||||||
|
bar
|
||||||
|
fi
|
||||||
|
");
|
||||||
|
let expected = String::from("
|
||||||
|
echo foo
|
||||||
|
if true; then
|
||||||
|
echo bar
|
||||||
|
fi
|
||||||
|
");
|
||||||
|
|
||||||
|
let result = expand_aliases(input, HashSet::new(), &l);
|
||||||
|
assert_eq!(result,expected)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expand_multiple_aliases() {
|
fn expand_multiple_aliases() {
|
||||||
write_logic(|l| l.insert_alias("foo", "echo foo"));
|
write_logic(|l| {
|
||||||
write_logic(|l| l.insert_alias("bar", "echo bar"));
|
l.insert_alias("foo", "echo foo");
|
||||||
write_logic(|l| l.insert_alias("biz", "echo biz"));
|
l.insert_alias("bar", "echo bar");
|
||||||
|
l.insert_alias("biz", "echo biz");
|
||||||
|
let input = String::from("foo; bar; biz");
|
||||||
|
|
||||||
let input = String::from("foo; bar; biz");
|
let result = expand_aliases(input, HashSet::new(), &l);
|
||||||
|
assert_eq!(result.as_str(),"echo foo; echo bar; echo biz");
|
||||||
let result = expand_aliases(input, HashSet::new());
|
});
|
||||||
assert_eq!(result.as_str(),"echo foo; echo bar; echo biz")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alias_in_arg_position() {
|
fn alias_in_arg_position() {
|
||||||
write_logic(|l| l.insert_alias("foo", "echo foo"));
|
write_logic(|l| {
|
||||||
|
l.insert_alias("foo", "echo foo");
|
||||||
|
let input = String::from("echo foo");
|
||||||
|
|
||||||
let input = String::from("echo foo");
|
let result = expand_aliases(input.clone(), HashSet::new(), &l);
|
||||||
|
assert_eq!(input,result);
|
||||||
let result = expand_aliases(input.clone(), HashSet::new());
|
l.clear_aliases();
|
||||||
assert_eq!(input,result)
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expand_recursive_alias() {
|
fn expand_recursive_alias() {
|
||||||
write_logic(|l| l.insert_alias("foo", "echo foo"));
|
write_logic(|l| {
|
||||||
write_logic(|l| l.insert_alias("bar", "foo bar"));
|
l.insert_alias("foo", "echo foo");
|
||||||
|
l.insert_alias("bar", "foo bar");
|
||||||
|
|
||||||
let input = String::from("bar");
|
let input = String::from("bar");
|
||||||
let result = expand_aliases(input, HashSet::new());
|
let result = expand_aliases(input, HashSet::new(), &l);
|
||||||
assert_eq!(result.as_str(),"echo foo bar")
|
assert_eq!(result.as_str(),"echo foo bar");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_infinite_recursive_alias() {
|
fn test_infinite_recursive_alias() {
|
||||||
write_logic(|l| l.insert_alias("foo", "foo bar"));
|
write_logic(|l| {
|
||||||
|
l.insert_alias("foo", "foo bar");
|
||||||
|
|
||||||
|
let input = String::from("foo");
|
||||||
|
let result = expand_aliases(input, HashSet::new(), &l);
|
||||||
|
assert_eq!(result.as_str(),"foo bar");
|
||||||
|
l.clear_aliases();
|
||||||
|
});
|
||||||
|
|
||||||
let input = String::from("foo");
|
|
||||||
let result = expand_aliases(input, HashSet::new());
|
|
||||||
assert_eq!(result.as_str(),"foo bar")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use getopt::get_opts_from_tokens;
|
use getopt::{get_opts, get_opts_from_tokens};
|
||||||
use parse::NdRule;
|
use parse::NdRule;
|
||||||
use tests::get_nodes;
|
use tests::get_nodes;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn lex_simple() {
|
fn lex_simple() {
|
||||||
let input = "echo hello world";
|
let input = "echo hello world";
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
use std::rc::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub use super::*;
|
||||||
|
use crate::libsh::error::{
|
||||||
|
Note, ShErr, ShErrKind
|
||||||
|
};
|
||||||
|
use crate::parse::{
|
||||||
|
node_operation, Node, NdRule, ParseStream,
|
||||||
|
lex::{
|
||||||
|
Tk, TkFlags, TkRule, LexFlags, LexStream
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use crate::expand::{
|
||||||
|
expand_aliases, unescape_str
|
||||||
|
};
|
||||||
|
use crate::state::{
|
||||||
|
write_logic, write_vars
|
||||||
|
};
|
||||||
|
|
||||||
use crate::parse::{lex::{LexFlags, LexStream}, node_operation, Node, ParseStream};
|
|
||||||
|
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use parse::{node_operation, NdRule, Node};
|
use super::*;
|
||||||
|
|
||||||
use super::super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_simple() {
|
fn parse_simple() {
|
||||||
@@ -170,13 +168,30 @@ esac";
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_case_nested() {
|
fn parse_case_nested() {
|
||||||
let input = "case foo in
|
let input = "case foo in
|
||||||
foo) if true; then
|
foo)
|
||||||
echo foo
|
if true; then
|
||||||
fi
|
while true; do
|
||||||
|
echo foo
|
||||||
|
done
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
bar) if false; then
|
bar)
|
||||||
echo bar
|
if false; then
|
||||||
fi
|
until false; do
|
||||||
|
case foo in
|
||||||
|
foo)
|
||||||
|
if true; then
|
||||||
|
echo foo
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
bar)
|
||||||
|
if false; then
|
||||||
|
echo foo
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac";
|
esac";
|
||||||
let tk_stream: Vec<_> = LexStream::new(Arc::new(input.to_string()), LexFlags::empty())
|
let tk_stream: Vec<_> = LexStream::new(Arc::new(input.to_string()), LexFlags::empty())
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
source: src/tests/error.rs
|
||||||
|
expression: err_fmt
|
||||||
|
---
|
||||||
|
Execution failed
|
||||||
|
[32mnote[0m: Execution failed for this reason
|
||||||
|
|
||||||
|
[32mnote[0m: Here is how to fix it: blah blah blah
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
source: src/tests/error.rs
|
||||||
|
expression: err_fmt
|
||||||
|
---
|
||||||
|
Execution failed
|
||||||
|
[32mnote[0m: Execution failed for this reason
|
||||||
|
|
||||||
|
[32mnote[0m: Here is how to fix it:
|
||||||
|
[36m[1m-[0m blah
|
||||||
|
[36m[1m-[0m blah
|
||||||
|
[36m[1m-[0m blah
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user