diff --git a/src/expand.rs b/src/expand.rs index c195727..9251c9c 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -394,10 +394,22 @@ impl FromStr for ParamExp { return Ok(ReplaceAllMatches(pattern.to_string(), repl.to_string())); } if let Some(rest) = s.strip_prefix('/') { - let mut parts = rest.splitn(2, '/'); - let pattern = parts.next().unwrap_or(""); - let repl = parts.next().unwrap_or(""); - return Ok(ReplaceFirstMatch(pattern.to_string(), repl.to_string())); + if let Some(rest) = rest.strip_prefix('%') { + let mut parts = rest.splitn(2, '/'); + let pattern = parts.next().unwrap_or(""); + let repl = parts.next().unwrap_or(""); + return Ok(ReplaceSuffix(pattern.to_string(), repl.to_string())); + } else if let Some(rest) = rest.strip_prefix('#') { + let mut parts = rest.splitn(2, '/'); + let pattern = parts.next().unwrap_or(""); + let repl = parts.next().unwrap_or(""); + return Ok(ReplacePrefix(pattern.to_string(), repl.to_string())); + } else { + let mut parts = rest.splitn(2, '/'); + let pattern = parts.next().unwrap_or(""); + let repl = parts.next().unwrap_or(""); + return Ok(ReplaceFirstMatch(pattern.to_string(), repl.to_string())); + } } // Fallback / assignment / alt @@ -464,6 +476,7 @@ pub fn perform_param_expansion(raw: &str) -> ShResult { '-' | '+' | '=' | + '/' | '?' => { rest.push(ch); rest.push_str(&chars.collect::()); @@ -582,7 +595,7 @@ pub fn perform_param_expansion(raw: &str) -> ShResult { ParamExp::RemShortestSuffix(suffix) => { let value = vars.get_var(&var_name); let pattern = Pattern::new(&suffix).unwrap(); - for i in 0..=value.len() { + for i in (0..=value.len()).rev() { let sliced = &value[i..]; if pattern.matches(sliced) { return Ok(value[..i].to_string()); @@ -593,7 +606,7 @@ pub fn perform_param_expansion(raw: &str) -> ShResult { ParamExp::RemLongestSuffix(suffix) => { let value = vars.get_var(&var_name); let pattern = Pattern::new(&suffix).unwrap(); - for i in (0..=value.len()).rev() { + for i in 0..=value.len() { let sliced = &value[i..]; if pattern.matches(sliced) { return Ok(value[..i].to_string()); @@ -679,21 +692,7 @@ fn glob_to_regex(glob: &str, anchored: bool) -> Regex { if anchored { regex.push('$'); } - Regex::new(®ex).unwrap() -} -fn glob_to_regex_unanchored(glob: &str) -> Regex { - let mut regex = String::new(); - for ch in glob.chars() { - match ch { - '*' => regex.push_str(".*"), - '?' => regex.push('.'), - '.' | '+' | '(' | ')' | '|' | '^' | '$' | '[' | ']' | '{' | '}' | '\\' => { - regex.push('\\'); - regex.push(ch); - } - _ => regex.push(ch), - } - } + flog!(DEBUG, regex); Regex::new(®ex).unwrap() } diff --git a/src/tests/expand.rs b/src/tests/expand.rs index cf03baa..30ab4bc 100644 --- a/src/tests/expand.rs +++ b/src/tests/expand.rs @@ -1,5 +1,7 @@ use std::collections::HashSet; +use crate::expand::perform_param_expansion; + use super::*; #[test] @@ -122,3 +124,179 @@ fn test_infinite_recursive_alias() { }); } + +#[test] +fn param_expansion_defaultunsetornull() { + write_vars(|v| { + v.set_var("foo", "foo", false); + v.set_var("set_var", "value", false); + }); + let result = perform_param_expansion("unset:-default").unwrap(); + assert_eq!(result, "default"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_defaultunset() { + write_vars(|v| { + v.set_var("foo", "foo", false); + v.set_var("set_var", "value", false); + }); + let result = perform_param_expansion("unset-default").unwrap(); + assert_eq!(result, "default"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_setdefaultunsetornull() { + write_vars(|v| { + v.set_var("foo", "foo", false); + v.set_var("set_var", "value", false); + }); + let result = perform_param_expansion("unset:=assigned").unwrap(); + assert_eq!(result, "assigned"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_setdefaultunset() { + write_vars(|v| { + v.set_var("foo", "foo", false); + v.set_var("set_var", "value", false); + }); + let result = perform_param_expansion("unset=assigned").unwrap(); + assert_eq!(result, "assigned"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_altsetnotnull() { + write_vars(|v| { + v.set_var("foo", "foo", false); + v.set_var("set_var", "value", false); + }); + let result = perform_param_expansion("set_var:+alt").unwrap(); + assert_eq!(result, "alt"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_altnotnull() { + write_vars(|v| { + v.set_var("foo", "foo", false); + v.set_var("set_var", "value", false); + }); + let result = perform_param_expansion("set_var+alt").unwrap(); + assert_eq!(result, "alt"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_len() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("#foo").unwrap(); + assert_eq!(result, "3"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_substr() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo:1").unwrap(); + assert_eq!(result, "oo"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_substrlen() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo:0:2").unwrap(); + assert_eq!(result, "fo"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_remshortestprefix() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo#f*").unwrap(); + assert_eq!(result, "oo"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_remlongestprefix() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo##f*").unwrap(); + assert_eq!(result, ""); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_remshortestsuffix() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo%*o").unwrap(); + assert_eq!(result, "fo"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_remlongestsuffix() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo%%*o").unwrap(); + assert_eq!(result, ""); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_replacefirstmatch() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo/foo/X").unwrap(); + assert_eq!(result, "X"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_replaceallmatches() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo//o/X").unwrap(); + assert_eq!(result, "fXX"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_replaceprefix() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo/#f/X").unwrap(); + assert_eq!(result, "Xoo"); + write_vars(|v| v.vars_mut().clear()); +} + +#[test] +fn param_expansion_replacesuffix() { + write_vars(|v| { + v.set_var("foo", "foo", false); + }); + let result = perform_param_expansion("foo/%o/X").unwrap(); + assert_eq!(result, "foX"); + write_vars(|v| v.vars_mut().clear()); +}