diff --git a/src/lib.rs b/src/lib.rs index 836cebf15172c902e75a4da869a76f27401b9861..c28a059ac955d78a353a66b8217207803c50748b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![allow(unknown_lints)] #![allow(while_let_on_iterator)] +#![feature(conservative_impl_trait)] + extern crate app_dirs; #[macro_use] extern crate bitflags; diff --git a/src/main.rs b/src/main.rs index c047def6bc151c1412883575273473a88b184bb1..d575fb6367f7fcb9f582119d801cc4ac2e83f81a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![allow(unknown_lints)] #![allow(while_let_on_iterator)] +#![feature(conservative_impl_trait)] // For a performance boost on Linux // #![feature(alloc_system)] diff --git a/src/parser/shell_expand/words/methods/strings.rs b/src/parser/shell_expand/words/methods/strings.rs index dfb8abb0403d907b83f063bfdfd9af00b4c8918c..b74d64d68cc01e80b8a16d164ce972cb2b122c4b 100644 --- a/src/parser/shell_expand/words/methods/strings.rs +++ b/src/parser/shell_expand/words/methods/strings.rs @@ -13,6 +13,27 @@ lazy_static! { static ref STRING_METHODS: StringMethodPlugins = methods::collect(); } +pub(crate) struct MethodArgs<'a, 'b, E: 'b + Expander> { + args: &'a str, + expand: &'b E, +} + +impl<'a, 'b, E: 'b + Expander> MethodArgs<'a, 'b, E> { + pub(crate) fn new(args: &'a str, expand: &'b E) -> MethodArgs<'a, 'b, E> { + MethodArgs { args, expand } + } + + pub(crate) fn join(self, pattern: &str) -> String { + unescape(expand_string(self.args, self.expand, false).join(pattern)) + } + + pub(crate) fn array<'c>(&'c self) -> impl Iterator<Item = String> + 'c { + ArgumentSplitter::new(self.args) + .flat_map(move |x| expand_string(x, self.expand, false).into_iter()) + .map(unescape) + } +} + /// Represents a method that operates on and returns a string #[derive(Debug, PartialEq, Clone)] pub(crate) struct StringMethod<'a> { @@ -31,16 +52,16 @@ pub(crate) struct StringMethod<'a> { impl<'a> StringMethod<'a> { pub(crate) fn handle<E: Expander>(&self, output: &mut String, expand: &E) { - let (variable, pattern) = (self.variable, self.pattern); + let variable = self.variable; + let pattern = MethodArgs::new(self.pattern, expand); macro_rules! string_eval { - ($variable:ident $method:tt $pattern:ident) => {{ - let pattern = unescape(expand_string($pattern, expand, false).join(" ")); + ($variable:ident $method:tt) => {{ + let pattern = pattern.join(" "); let is_true = if let Some(value) = expand.variable($variable, false) { value.$method(&pattern) } else if is_expression($variable) { - expand_string($variable, expand, false).join($pattern) - .$method(&pattern) + expand_string($variable, expand, false).join(" ").$method(&pattern) } else { false }; @@ -54,7 +75,7 @@ impl<'a> StringMethod<'a> { output.push_str(Path::new(&value).$method() .and_then(|os_str| os_str.to_str()).unwrap_or(value.as_str())); } else if is_expression(variable) { - let word = expand_string(variable, expand, false).join(pattern); + let word = expand_string(variable, expand, false).join(" "); output.push_str(Path::new(&word).$method() .and_then(|os_str| os_str.to_str()).unwrap_or(word.as_str())); } @@ -66,41 +87,35 @@ impl<'a> StringMethod<'a> { if let Some(value) = expand.variable(variable, false) { output.push_str(value.$method().as_str()); } else if is_expression(variable) { - let word = expand_string(variable, expand, false).join(pattern); + let word = expand_string(variable, expand, false).join(" "); output.push_str(word.$method().as_str()); } }} } match self.method { - "ends_with" => string_eval!(variable ends_with pattern), - "contains" => string_eval!(variable contains pattern), - "starts_with" => string_eval!(variable starts_with pattern), + "ends_with" => string_eval!(variable ends_with), + "contains" => string_eval!(variable contains), + "starts_with" => string_eval!(variable starts_with), "basename" => path_eval!(file_name), "extension" => path_eval!(extension), "filename" => path_eval!(file_stem), "parent" => path_eval!(parent), "to_lowercase" => string_case!(to_lowercase), "to_uppercase" => string_case!(to_uppercase), - "repeat" => { - let pattern = unescape(expand_string(pattern, expand, false).join(" ")); - match pattern.parse::<usize>() { - Ok(repeat) => if let Some(value) = expand.variable(variable, false) { - output.push_str(&value.repeat(repeat)); - } else if is_expression(variable) { - let value = expand_string(variable, expand, false).join(" "); - output.push_str(&value.repeat(repeat)); - }, - Err(_) => { - eprintln!("ion: value supplied to $repeat() is not a valid number"); - } + "repeat" => match pattern.join(" ").parse::<usize>() { + Ok(repeat) => if let Some(value) = expand.variable(variable, false) { + output.push_str(&value.repeat(repeat)); + } else if is_expression(variable) { + let value = expand_string(variable, expand, false).join(" "); + output.push_str(&value.repeat(repeat)); + }, + Err(_) => { + eprintln!("ion: value supplied to $repeat() is not a valid number"); } - } + }, "replace" => { - let pattern = ArgumentSplitter::new(pattern) - .map(|x| unescape(expand_string(x, expand, false).join(" "))) - .take(2) - .collect::<Vec<_>>(); + let pattern = pattern.array().take(2).collect::<Vec<_>>(); if pattern.len() == 2 { if let Some(value) = expand.variable(variable, false) { output.push_str(&value.replace(pattern[0].as_str(), pattern[1].as_str())); @@ -113,10 +128,7 @@ impl<'a> StringMethod<'a> { } } "replacen" => { - let pattern = ArgumentSplitter::new(pattern) - .map(|x| unescape(expand_string(x, expand, false).join(" "))) - .take(3) - .collect::<Vec<_>>(); + let pattern = pattern.array().take(3).collect::<Vec<_>>(); if pattern.len() == 3 { if let Ok(nth) = pattern[2].as_str().parse::<usize>() { if let Some(value) = expand.variable(variable, false) { @@ -137,7 +149,7 @@ impl<'a> StringMethod<'a> { } } "join" => { - let pattern = unescape(expand_string(pattern, expand, false).join(" ")); + let pattern = pattern.join(" "); if let Some(array) = expand.array(variable, Select::All) { slice(output, array.join(&pattern), self.selection.clone()); } else if is_expression(variable) { @@ -155,30 +167,29 @@ impl<'a> StringMethod<'a> { let count = UnicodeSegmentation::graphemes(value.as_str(), true).count(); output.push_str(&count.to_string()); } else if is_expression(variable) { - let word = expand_string(variable, expand, false).join(pattern); + let word = expand_string(variable, expand, false).join(" "); let count = UnicodeSegmentation::graphemes(word.as_str(), true).count(); output.push_str(&count.to_string()); }, "len_bytes" => if let Some(value) = expand.variable(variable, false) { output.push_str(&value.as_bytes().len().to_string()); } else if is_expression(variable) { - let word = expand_string(variable, expand, false).join(pattern); + let word = expand_string(variable, expand, false).join(" "); output.push_str(&word.as_bytes().len().to_string()); }, "reverse" => if let Some(value) = expand.variable(variable, false) { let rev_graphs = UnicodeSegmentation::graphemes(value.as_str(), true).rev(); output.push_str(rev_graphs.collect::<String>().as_str()); } else if is_expression(variable) { - let word = expand_string(variable, expand, false).join(pattern); + let word = expand_string(variable, expand, false).join(" "); let rev_graphs = UnicodeSegmentation::graphemes(word.as_str(), true).rev(); output.push_str(rev_graphs.collect::<String>().as_str()); }, "find" => { - let pattern = unescape(expand_string(pattern, expand, false).join(" ")); let out = if let Some(value) = expand.variable(variable, false) { - value.find(&pattern) + value.find(&pattern.join(" ")) } else if is_expression(variable) { - expand_string(variable, expand, false).join(" ").find(&pattern) + expand_string(variable, expand, false).join(" ").find(&pattern.join(" ")) } else { None };