From 41ec1aa8d99bce8bf7c3b8e52ca0fe559a6a983c Mon Sep 17 00:00:00 2001 From: Xavier L'Heureux <xavier.lheureux@icloud.com> Date: Fri, 5 Jul 2019 08:48:01 -0400 Subject: [PATCH] Expand lazily the indexes and separate the parser from expansion --- src/lib/expansion/methods/arrays.rs | 131 ++++++---------- src/lib/expansion/methods/strings.rs | 132 +++++++--------- src/lib/expansion/mod.rs | 187 +++++++++++++---------- src/lib/expansion/words/mod.rs | 215 ++++++++++++--------------- src/lib/expansion/words/tests.rs | 120 ++++++--------- src/lib/shell/shell_expand.rs | 16 +- src/lib/shell/variables.rs | 12 +- 7 files changed, 355 insertions(+), 458 deletions(-) diff --git a/src/lib/expansion/methods/arrays.rs b/src/lib/expansion/methods/arrays.rs index e78d8268..f95c451f 100644 --- a/src/lib/expansion/methods/arrays.rs +++ b/src/lib/expansion/methods/arrays.rs @@ -1,9 +1,5 @@ use super::{ - super::{ - is_expression, - words::{Select, SelectWithSize}, - Error, Expander, Index, - }, + super::{is_expression, words::Select, Error, Expander, ExpanderInternal, Index}, strings::unescape, MethodError, Pattern, }; @@ -16,7 +12,7 @@ pub struct ArrayMethod<'a> { method: &'a str, variable: &'a str, pattern: Pattern<'a>, - selection: Select<types::Str>, + selection: Option<&'a str>, } impl<'a> ArrayMethod<'a> { @@ -24,7 +20,7 @@ impl<'a> ArrayMethod<'a> { method: &'a str, variable: &'a str, pattern: Pattern<'a>, - selection: Select<types::Str>, + selection: Option<&'a str>, ) -> Self { Self { method, variable, pattern, selection } } @@ -41,30 +37,28 @@ impl<'a> ArrayMethod<'a> { fn chars<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> { let variable = self.resolve_var(expand_func)?; - let len = variable.chars().count(); - Ok(variable.chars().map(|c| types::Str::from(c.to_string())).select(&self.selection, len)) + expand_func + .slice_array(variable.chars().map(|c| types::Str::from(c.to_string())), &self.selection) } fn bytes<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> { let variable = self.resolve_var(expand_func)?; - let len = variable.len(); - Ok(variable.bytes().map(|b| types::Str::from(b.to_string())).select(&self.selection, len)) + expand_func + .slice_array(variable.bytes().map(|b| types::Str::from(b.to_string())), &self.selection) } fn map_keys<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Args, Error<E::Error>> { - expand_func.map_keys(self.variable, &self.selection) + expand_func.slice_array(expand_func.map_keys(self.variable)?.into_iter(), &self.selection) } fn map_values<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Args, Error<E::Error>> { - expand_func.map_values(self.variable, &self.selection) + expand_func.slice_array(expand_func.map_values(self.variable)?.into_iter(), &self.selection) } fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> { let variable = self.resolve_var(expand_func)?; - let graphemes: Vec<types::Str> = - UnicodeSegmentation::graphemes(variable.as_str(), true).map(From::from).collect(); - let len = graphemes.len(); - Ok(graphemes.into_iter().select(&self.selection, len)) + let graphemes = UnicodeSegmentation::graphemes(variable.as_str(), true); + expand_func.slice_array(graphemes, &self.selection) } fn split_at<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> { @@ -98,19 +92,17 @@ impl<'a> ArrayMethod<'a> { fn split<E: Expander>(&self, expand_func: &E) -> Result<Args, Error<E::Error>> { let variable = self.resolve_var(expand_func)?; - let data: Args = match self.pattern { - Pattern::Whitespace => variable - .split(char::is_whitespace) - .filter(|x| !x.is_empty()) - .map(From::from) - .collect(), - Pattern::StringPattern(pattern) => variable - .split(unescape(&expand_func.expand_string(pattern)?.join(" ")).as_str()) - .map(From::from) - .collect(), - }; - let len = data.len(); - Ok(data.into_iter().select(&self.selection, len)) + match self.pattern { + Pattern::Whitespace => { + let data = variable.split(char::is_whitespace).filter(|x| !x.is_empty()); + expand_func.slice_array(data, &self.selection) + } + Pattern::StringPattern(pattern) => { + let escape = unescape(&expand_func.expand_string(pattern)?.join(" ")); + let data = variable.split(escape.as_str()); + expand_func.slice_array(data, &self.selection) + } + } } #[inline] @@ -168,16 +160,12 @@ impl<'a> ArrayMethod<'a> { #[cfg(test)] mod test { use super::*; - use crate::{ - expansion::test::DummyExpander, - ranges::{Index, Range}, - types, - }; + use crate::{expansion::test::DummyExpander, types}; #[test] fn test_split_string_all() { let mut output = types::Str::new(); - let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Select::All); + let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), None); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FO AR"); } @@ -185,7 +173,7 @@ mod test { #[test] fn test_split_whitespace_all() { let mut output = types::Str::new(); - let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Select::All); + let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, None); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOO BAR"); } @@ -193,12 +181,7 @@ mod test { #[test] fn test_split_string_index_forward() { let mut output = types::Str::new(); - let method = ArrayMethod::new( - "split", - "$FOO", - Pattern::StringPattern("OB"), - Select::Index(Index::Forward(1)), - ); + let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Some("1")); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "AR"); } @@ -206,12 +189,7 @@ mod test { #[test] fn test_split_whitespace_index_forward() { let mut output = types::Str::new(); - let method = ArrayMethod::new( - "split", - "$SPACEDFOO", - Pattern::Whitespace, - Select::Index(Index::Forward(1)), - ); + let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("1")); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "BAR"); } @@ -219,12 +197,7 @@ mod test { #[test] fn test_split_string_index_backward() { let mut output = types::Str::new(); - let method = ArrayMethod::new( - "split", - "$FOO", - Pattern::StringPattern("OB"), - Select::Index(Index::Backward(1)), - ); + let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Some("-2")); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FO"); } @@ -232,12 +205,7 @@ mod test { #[test] fn test_split_whitespace_index_backward() { let mut output = types::Str::new(); - let method = ArrayMethod::new( - "split", - "$SPACEDFOO", - Pattern::Whitespace, - Select::Index(Index::Backward(1)), - ); + let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("-2")); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOO"); } @@ -245,12 +213,7 @@ mod test { #[test] fn test_split_string_range() { let mut output = types::Str::new(); - let method = ArrayMethod::new( - "split", - "$FOO", - Pattern::StringPattern("OB"), - Select::Range(Range::from(Index::Forward(0))), - ); + let method = ArrayMethod::new("split", "$FOO", Pattern::StringPattern("OB"), Some("0..")); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FO AR"); } @@ -258,12 +221,7 @@ mod test { #[test] fn test_split_whitespace_range() { let mut output = types::Str::new(); - let method = ArrayMethod::new( - "split", - "$SPACEDFOO", - Pattern::Whitespace, - Select::Range(Range::from(Index::Forward(0))), - ); + let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("0..")); method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOO BAR"); } @@ -271,42 +229,39 @@ mod test { #[test] fn test_split_key() { let mut output = types::Str::new(); - let method = - ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Select::Key("1".into())); + let method = ArrayMethod::new("split", "$SPACEDFOO", Pattern::Whitespace, Some("\"1\"")); method.handle(&mut output, &DummyExpander).unwrap(); - assert_eq!(&*output, ""); + assert_eq!(&*output, "BAR"); } #[test] fn test_split_at_failing_whitespace() { - let method = ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::Whitespace, Select::All); + let method = ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::Whitespace, None); assert!(method.handle_as_array(&DummyExpander).is_err()); } #[test] fn test_split_at_failing_no_number() { - let method = - ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("a"), Select::All); + let method = ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("a"), None); assert!(method.handle_as_array(&DummyExpander).is_err()); } #[test] fn test_split_at_failing_out_of_bound() { let method = - ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("100"), Select::All); + ArrayMethod::new("split_at", "$SPACEDFOO", Pattern::StringPattern("100"), None); assert!(method.handle_as_array(&DummyExpander).is_err()); } #[test] fn test_split_at_succeeding() { - let method = ArrayMethod::new("split_at", "$FOO", Pattern::StringPattern("3"), Select::All); + let method = ArrayMethod::new("split_at", "$FOO", Pattern::StringPattern("3"), None); assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), args!["FOO", "BAR"]); } #[test] fn test_graphemes() { - let method = - ArrayMethod::new("graphemes", "$FOO", Pattern::StringPattern("3"), Select::All); + let method = ArrayMethod::new("graphemes", "$FOO", Pattern::StringPattern("3"), None); assert_eq!( method.handle_as_array(&DummyExpander).unwrap(), args!["F", "O", "O", "B", "A", "R"] @@ -315,7 +270,7 @@ mod test { #[test] fn test_bytes() { - let method = ArrayMethod::new("bytes", "$FOO", Pattern::StringPattern("3"), Select::All); + let method = ArrayMethod::new("bytes", "$FOO", Pattern::StringPattern("3"), None); assert_eq!( method.handle_as_array(&DummyExpander).unwrap(), args!["70", "79", "79", "66", "65", "82"] @@ -324,7 +279,7 @@ mod test { #[test] fn test_chars() { - let method = ArrayMethod::new("chars", "$FOO", Pattern::StringPattern("3"), Select::All); + let method = ArrayMethod::new("chars", "$FOO", Pattern::StringPattern("3"), None); assert_eq!( method.handle_as_array(&DummyExpander).unwrap(), args!["F", "O", "O", "B", "A", "R"] @@ -333,15 +288,13 @@ mod test { #[test] fn test_lines() { - let method = - ArrayMethod::new("lines", "$MULTILINE", Pattern::StringPattern("3"), Select::All); + let method = ArrayMethod::new("lines", "$MULTILINE", Pattern::StringPattern("3"), None); assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), args!["FOO", "BAR"]); } #[test] fn test_reverse() { - let method = - ArrayMethod::new("reverse", "@ARRAY", Pattern::StringPattern("3"), Select::All); + let method = ArrayMethod::new("reverse", "@ARRAY", Pattern::StringPattern("3"), None); assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), args!["c", "b", "a"]); } } diff --git a/src/lib/expansion/methods/strings.rs b/src/lib/expansion/methods/strings.rs index 0dc64b7b..806634a4 100644 --- a/src/lib/expansion/methods/strings.rs +++ b/src/lib/expansion/methods/strings.rs @@ -87,7 +87,7 @@ pub struct StringMethod<'a> { /// Pattern to use for certain methods pub pattern: &'a str, /// Selection to use to control the output of this method - pub selection: Select<types::Str>, + pub selection: Option<&'a str>, } impl<'a> StringMethod<'a> { @@ -223,18 +223,12 @@ impl<'a> StringMethod<'a> { "join" => { let pattern = pattern.join(" ")?; match expand.array(variable, &Select::All) { - Ok(array) => <E as ExpanderInternal>::slice( + Ok(array) => expand.slice(output, array.join(&pattern), &self.selection)?, + Err(Error::VarNotFound) if is_expression(variable) => expand.slice( output, - array.join(&pattern), + expand.expand_string(variable)?.join(&pattern), &self.selection, - ), - Err(Error::VarNotFound) if is_expression(variable) => { - <E as ExpanderInternal>::slice( - output, - expand.expand_string(variable)?.join(&pattern), - &self.selection, - ) - } + )?, Err(why) => return Err(why), } } @@ -368,7 +362,7 @@ mod test { method: "ends_with", variable: "$FOO", pattern: "\"BAR\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "1"); @@ -381,7 +375,7 @@ mod test { method: "ends_with", variable: "$FOO", pattern: "\"BA\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "0"); @@ -394,7 +388,7 @@ mod test { method: "contains", variable: "$FOO", pattern: "\"OBA\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "1"); @@ -407,7 +401,7 @@ mod test { method: "contains", variable: "$FOO", pattern: "\"OBI\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "0"); @@ -420,7 +414,7 @@ mod test { method: "starts_with", variable: "$FOO", pattern: "\"FOO\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "1"); @@ -433,7 +427,7 @@ mod test { method: "starts_with", variable: "$FOO", pattern: "\"OO\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "0"); @@ -446,7 +440,7 @@ mod test { method: "basename", variable: "\"/home/redox/file.txt\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "file.txt"); @@ -459,7 +453,7 @@ mod test { method: "extension", variable: "\"/home/redox/file.txt\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "txt"); @@ -472,7 +466,7 @@ mod test { method: "filename", variable: "\"/home/redox/file.txt\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "file"); @@ -485,7 +479,7 @@ mod test { method: "parent", variable: "\"/home/redox/file.txt\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "/home/redox"); @@ -498,7 +492,7 @@ mod test { method: "to_lowercase", variable: "\"Ford Prefect\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "ford prefect"); @@ -511,7 +505,7 @@ mod test { method: "to_uppercase", variable: "\"Ford Prefect\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FORD PREFECT"); @@ -524,7 +518,7 @@ mod test { method: "trim", variable: "\" Foo Bar \"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "Foo Bar"); @@ -533,12 +527,8 @@ mod test { #[test] fn test_trim_with_variable() { let mut output = types::Str::new(); - let method = StringMethod { - method: "trim", - variable: "$BAZ", - pattern: "", - selection: Select::All, - }; + let method = + StringMethod { method: "trim", variable: "$BAZ", pattern: "", selection: None }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "BARBAZ"); } @@ -550,7 +540,7 @@ mod test { method: "trim_right", variable: "\" Foo Bar \"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, " Foo Bar"); @@ -563,7 +553,7 @@ mod test { method: "trim_right", variable: "$BAZ", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, " BARBAZ"); @@ -576,7 +566,7 @@ mod test { method: "trim_left", variable: "\" Foo Bar \"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "Foo Bar "); @@ -589,7 +579,7 @@ mod test { method: "trim_left", variable: "$BAZ", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "BARBAZ "); @@ -602,7 +592,7 @@ mod test { method: "repeat", variable: "$FOO", pattern: "2", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOOBARFOOBAR"); @@ -616,7 +606,7 @@ mod test { method: "repeat", variable: "$FOO", pattern: "-2", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); } @@ -628,7 +618,7 @@ mod test { method: "replace", variable: "$FOO", pattern: "[\"FOO\" \"BAR\"]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "BARBAR"); @@ -642,7 +632,7 @@ mod test { method: "replace", variable: "$FOO", pattern: "[]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); } @@ -654,7 +644,7 @@ mod test { method: "replacen", variable: "\"FOO$FOO\"", pattern: "[\"FOO\" \"BAR\" 1]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "BARFOOBAR"); @@ -668,7 +658,7 @@ mod test { method: "replacen", variable: "$FOO", pattern: "[]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); } @@ -680,7 +670,7 @@ mod test { method: "regex_replace", variable: "$FOO", pattern: "[\"^F\" \"f\"]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "fOOBAR"); @@ -693,7 +683,7 @@ mod test { method: "regex_replace", variable: "$FOO", pattern: "[\"^f\" \"F\"]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOOBAR"); @@ -706,7 +696,7 @@ mod test { method: "join", variable: "[\"FOO\" \"BAR\"]", pattern: "\" \"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOO BAR"); @@ -719,7 +709,7 @@ mod test { method: "join", variable: "[\"FOO\" \"BAR\"]", pattern: "[\"-\" \"-\"]", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOO- -BAR"); @@ -728,12 +718,8 @@ mod test { #[test] fn test_len_with_array() { let mut output = types::Str::new(); - let method = StringMethod { - method: "len", - variable: "[\"1\"]", - pattern: "", - selection: Select::All, - }; + let method = + StringMethod { method: "len", variable: "[\"1\"]", pattern: "", selection: None }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "1"); } @@ -741,12 +727,8 @@ mod test { #[test] fn test_len_with_string() { let mut output = types::Str::new(); - let method = StringMethod { - method: "len", - variable: "\"FOO\"", - pattern: "", - selection: Select::All, - }; + let method = + StringMethod { method: "len", variable: "\"FOO\"", pattern: "", selection: None }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "3"); } @@ -754,12 +736,8 @@ mod test { #[test] fn test_len_with_variable() { let mut output = types::Str::new(); - let method = StringMethod { - method: "len", - variable: "$FOO", - pattern: "", - selection: Select::All, - }; + let method = + StringMethod { method: "len", variable: "$FOO", pattern: "", selection: None }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "6"); } @@ -771,7 +749,7 @@ mod test { method: "len_bytes", variable: "$FOO", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "6"); @@ -784,7 +762,7 @@ mod test { method: "len_bytes", variable: "\"oh là là \"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "10"); @@ -797,7 +775,7 @@ mod test { method: "reverse", variable: "$FOO", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "RABOOF"); @@ -810,7 +788,7 @@ mod test { method: "reverse", variable: "\"FOOBAR\"", pattern: "", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "RABOOF"); @@ -823,7 +801,7 @@ mod test { method: "find", variable: "$FOO", pattern: "\"O\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "1"); @@ -836,7 +814,7 @@ mod test { method: "find", variable: "$FOO", pattern: "\"L\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "-1"); @@ -849,7 +827,7 @@ mod test { method: "or", variable: "$NDIUKFBINCF", pattern: "\"baz\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "baz"); @@ -862,7 +840,7 @@ mod test { method: "or", variable: "$EMPTY", pattern: "\"baz\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "baz"); @@ -875,7 +853,7 @@ mod test { method: "or", variable: "$FOO", pattern: "\"baz\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOOBAR"); @@ -888,7 +866,7 @@ mod test { method: "or", variable: "$EMPTY", pattern: "\"bar\", \"baz\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "bar"); @@ -901,7 +879,7 @@ mod test { method: "or", variable: "$EMPTY", pattern: "\"\", \"baz\"", - selection: Select::All, + selection: None, }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "baz"); @@ -910,12 +888,8 @@ mod test { #[test] fn test_or_no_pattern() { let mut output = types::Str::new(); - let method = StringMethod { - method: "or", - variable: "$FOO", - pattern: "\"\"", - selection: Select::All, - }; + let method = + StringMethod { method: "or", variable: "$FOO", pattern: "\"\"", selection: None }; method.handle(&mut output, &DummyExpander).unwrap(); assert_eq!(&*output, "FOOBAR"); } diff --git a/src/lib/expansion/mod.rs b/src/lib/expansion/mod.rs index 8033b628..9bded892 100644 --- a/src/lib/expansion/mod.rs +++ b/src/lib/expansion/mod.rs @@ -129,28 +129,13 @@ pub trait Expander: Sized { /// Expand a subshell expression. fn command(&self, _command: &str) -> Result<types::Str, Self::Error>; /// Iterating upon key-value maps. - fn map_keys(&self, _name: &str, _select: &Select<types::Str>) -> Result<Args, Self::Error>; + fn map_keys(&self, _name: &str) -> Result<Args, Self::Error>; /// Iterating upon key-value maps. - fn map_values(&self, _name: &str, _select: &Select<types::Str>) -> Result<Args, Self::Error>; + fn map_values(&self, _name: &str) -> Result<Args, Self::Error>; /// Get a string that exists in the shell. fn get_string(&self, value: &str) -> Result<types::Str, Self::Error> { Ok(self.expand_string(value)?.join(" ").into()) } - /// Select the proper values from an iterator - fn select<I: Iterator<Item = types::Str>>( - vals: I, - select: &Select<types::Str>, - n: usize, - ) -> Option<Args> { - match select { - Select::All => Some(vals.collect()), - Select::Range(range) => range - .bounds(n) - .filter(|&(start, _)| n > start) - .map(|(start, length)| vals.skip(start).take(length).collect()), - _ => None, - } - } /// Get an array that exists in the shell. fn get_array(&self, value: &str) -> Result<Args, Self::Error> { self.expand_string(value) } @@ -166,8 +151,7 @@ pub trait Expander: Sized { let mut token_buffer = Vec::new(); let mut contains_brace = false; - for word in WordIterator::new(original, self, true) { - let word = word?; + for word in WordIterator::new(original, true) { if let WordToken::Brace(_) = word { contains_brace = true; } @@ -181,14 +165,14 @@ pub trait Expander: Sized { impl<T: Expander> ExpanderInternal for T {} trait ExpanderInternal: Expander { - fn expand_process( + fn expand_process<'a>( &self, current: &mut types::Str, command: &str, - selection: &Select<types::Str>, + selection: &Option<&'a str>, ) -> Result<(), Self::Error> { - self.command(command) - .map(|result| Self::slice(current, result.trim_end_matches('\n'), selection)) + let result = self.command(command)?; + self.slice(current, result.trim_end_matches('\n'), selection) } fn expand_brace( @@ -224,8 +208,14 @@ trait ExpanderInternal: Expander { fn array_expand( &self, elements: &[&str], - selection: &Select<types::Str>, + selection: &Option<&str>, ) -> Result<Args, Self::Error> { + let selection = if let Some(selection) = selection { + let value = self.expand_string(selection)?.join(" "); + value.parse::<Select<types::Str>>().map_err(|_| Error::IndexParsingError(value))? + } else { + Select::All + }; match selection { Select::All => { let mut collected = Args::new(); @@ -234,8 +224,8 @@ trait ExpanderInternal: Expander { } Ok(collected) } - Select::Index(index) => self.array_nth(elements, *index).map(|el| args![el]), - Select::Range(range) => self.array_range(elements, *range), + Select::Index(index) => self.array_nth(elements, index).map(|el| args![el]), + Select::Range(range) => self.array_range(elements, range), Select::Key(_) => Err(Error::OutOfBound), } } @@ -277,41 +267,70 @@ trait ExpanderInternal: Expander { } } - fn slice<S: AsRef<str>>(output: &mut types::Str, expanded: S, selection: &Select<types::Str>) { - match selection { - Select::All => output.push_str(expanded.as_ref()), - Select::Index(Index::Forward(id)) => { - if let Some(character) = - UnicodeSegmentation::graphemes(expanded.as_ref(), true).nth(*id) - { - output.push_str(character); + fn slice_array<'a, S: Into<types::Str>, T: Iterator<Item = S>>( + &self, + expanded: T, + selection: &Option<&'a str>, + ) -> Result<Args, Self::Error> { + if let Some(selection) = selection { + let value = self.expand_string(selection)?.join(" "); + let selection = + value.parse::<Select<types::Str>>().map_err(|_| Error::IndexParsingError(value))?; + let expanded: Vec<_> = expanded.collect(); + let len = expanded.len(); + Ok(expanded.into_iter().map(Into::into).select(&selection, len)) + } else { + Ok(expanded.map(Into::into).collect()) + } + } + + fn slice<'a, S: AsRef<str>>( + &self, + output: &mut types::Str, + expanded: S, + selection: &Option<&'a str>, + ) -> Result<(), Self::Error> { + if let Some(selection) = selection { + let value = self.expand_string(selection)?.join(" "); + let selection = + value.parse::<Select<types::Str>>().map_err(|_| Error::IndexParsingError(value))?; + match selection { + Select::All => output.push_str(expanded.as_ref()), + Select::Index(Index::Forward(id)) => { + if let Some(character) = + UnicodeSegmentation::graphemes(expanded.as_ref(), true).nth(id) + { + output.push_str(character); + } } - } - Select::Index(Index::Backward(id)) => { - if let Some(character) = - UnicodeSegmentation::graphemes(expanded.as_ref(), true).rev().nth(*id) - { - output.push_str(character); + Select::Index(Index::Backward(id)) => { + if let Some(character) = + UnicodeSegmentation::graphemes(expanded.as_ref(), true).rev().nth(id) + { + output.push_str(character); + } } - } - Select::Range(range) => { - let graphemes = UnicodeSegmentation::graphemes(expanded.as_ref(), true); - if let Some((start, length)) = range.bounds(graphemes.clone().count()) { - graphemes.skip(start).take(length).for_each(|str| { - output.push_str(str.as_ref()); - }); + Select::Range(range) => { + let graphemes = UnicodeSegmentation::graphemes(expanded.as_ref(), true); + if let Some((start, length)) = range.bounds(graphemes.clone().count()) { + graphemes.skip(start).take(length).for_each(|str| { + output.push_str(str.as_ref()); + }); + } } + Select::Key(_) => (), } - Select::Key(_) => (), - } + } else { + output.push_str(expanded.as_ref()) + }; + Ok(()) } fn expand_string_no_glob(&self, original: &str) -> Result<Args, Self::Error> { let mut token_buffer = Vec::new(); let mut contains_brace = false; - for word in WordIterator::new(original, self, false) { - let word = word?; + for word in WordIterator::new(original, false) { if let WordToken::Brace(_) = word { contains_brace = true; } @@ -329,13 +348,14 @@ trait ExpanderInternal: Expander { WordToken::Array(ref elements, ref index) => { self.array_expand(elements, index).map_err(Into::into) } - WordToken::ArrayVariable(array, quoted, Select::Key(ref key)) if key.contains(' ') => { + WordToken::ArrayVariable(array, quoted, Some(ref key)) if key.contains(' ') => { if quoted { let mut output = types::Str::new(); for index in key.split(' ') { - let select = index + let value = self.expand_string(index)?.join(" "); + let select = value .parse::<Select<types::Str>>() - .map_err(|_| Error::IndexParsingError(index.into()))?; + .map_err(|_| Error::IndexParsingError(value))?; let _ = write!( &mut output, "{}", @@ -348,16 +368,25 @@ trait ExpanderInternal: Expander { } else { let mut out = Args::with_capacity(10); for index in key.split(' ') { - let select = index + let value = self.expand_string(index)?.join(" "); + let select = value .parse::<Select<types::Str>>() - .map_err(|_| Error::IndexParsingError(index.into()))?; + .map_err(|_| Error::IndexParsingError(value))?; out.extend(self.array(array, &select)?); } Ok(out) } } WordToken::ArrayVariable(array, quoted, ref index) => { - let array = self.array(array, index)?; + let index = if let Some(index) = index { + let value = self.expand_string(index)?.join(" "); + value + .parse::<Select<types::Str>>() + .map_err(|_| Error::IndexParsingError(value))? + } else { + Select::All + }; + let array = self.array(array, &index)?; if quoted { Ok(args![types::Str::from(array.join(" "))]) } else { @@ -366,22 +395,17 @@ trait ExpanderInternal: Expander { } WordToken::ArrayProcess(command, quoted, ref index) => { crate::IonPool::string(|output| { - self.expand_process(output, command, &Select::All)?; + self.expand_process(output, command, &None)?; if quoted { Ok(args!(format!( "{}", - output - .split_whitespace() - .select::<Vec<_>, _>(index, output.split_whitespace().count()) + self.slice_array(output.split_whitespace(), index)? .into_iter() .format(" ") ))) } else { - Ok(output - .split_whitespace() - .map(From::from) - .select::<Args, _>(index, output.split_whitespace().count())) + self.slice_array(output.split_whitespace(), index) } }) } @@ -411,7 +435,7 @@ trait ExpanderInternal: Expander { self.expand_process(&mut output, command, index)? } WordToken::Variable(text, ref index) => { - Self::slice(&mut output, self.string(text)?, index); + self.slice(&mut output, self.string(text)?, index)?; } WordToken::Arithmetic(s) => self.expand_arithmetic(&mut output, s), _ => unreachable!(), @@ -499,7 +523,7 @@ trait ExpanderInternal: Expander { self.array_expand(elements, index)?.iter().format(" ") ); } - WordToken::ArrayVariable(array, _, Select::Key(ref key)) if key.contains(' ') => { + WordToken::ArrayVariable(array, _, Some(ref key)) if key.contains(' ') => { for index in key.split(' ') { let select = index .parse::<Select<types::Str>>() @@ -514,7 +538,16 @@ trait ExpanderInternal: Expander { output.pop(); // Pop out the last unneeded whitespace token } WordToken::ArrayVariable(array, _, ref index) => { - let _ = write!(&mut output, "{}", self.array(array, index)?.iter().format(" ")); + let index = if let Some(index) = index { + let value = self.expand_string(index)?.join(" "); + value + .parse::<Select<types::Str>>() + .map_err(|_| Error::IndexParsingError(value))? + } else { + Select::All + }; + let _ = + write!(&mut output, "{}", self.array(array, &index)?.iter().format(" ")); } WordToken::ArrayProcess(command, _, ref index) | WordToken::Process(command, ref index) => { @@ -542,7 +575,7 @@ trait ExpanderInternal: Expander { output.push_str(text); } WordToken::Variable(text, ref index) => { - Self::slice(&mut output, self.string(text)?, index); + self.slice(&mut output, self.string(text)?, index)?; } WordToken::Arithmetic(s) => self.expand_arithmetic(&mut output, s), } @@ -674,19 +707,11 @@ pub(crate) mod test { fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) } - fn map_keys<'a>( - &'a self, - _name: &str, - _select: &Select<types::Str>, - ) -> Result<Args, Self::Error> { + fn map_keys<'a>(&'a self, _name: &str) -> Result<Args, Self::Error> { Err(Error::VarNotFound) } - fn map_values<'a>( - &'a self, - _name: &str, - _select: &Select<types::Str>, - ) -> Result<Args, Self::Error> { + fn map_values<'a>(&'a self, _name: &str) -> Result<Args, Self::Error> { Err(Error::VarNotFound) } } @@ -696,12 +721,12 @@ pub(crate) mod test { let mut output = types::Str::new(); let line = " Mary had\ta little \n\t lamb😉😉\t"; - DummyExpander.expand_process(&mut output, line, &Select::All).unwrap(); + DummyExpander.expand_process(&mut output, line, &None).unwrap(); assert_eq!(output.as_str(), line); output.clear(); let line = "foo not bar😉😉\n\n"; - DummyExpander.expand_process(&mut output, line, &Select::All).unwrap(); + DummyExpander.expand_process(&mut output, line, &None).unwrap(); assert_eq!(output.as_str(), "foo not bar😉😉"); } diff --git a/src/lib/expansion/words/mod.rs b/src/lib/expansion/words/mod.rs index 9409b5a3..da1e5453 100644 --- a/src/lib/expansion/words/mod.rs +++ b/src/lib/expansion/words/mod.rs @@ -1,12 +1,9 @@ #[cfg(test)] mod tests; -use super::{ - methods::{ArrayMethod, Pattern, StringMethod}, - Expander, Result, -}; +use super::methods::{ArrayMethod, Pattern, StringMethod}; +use crate::parser::lexers::ArgumentSplitter; pub use crate::ranges::{Select, SelectWithSize}; -use crate::{parser::lexers::ArgumentSplitter, types}; use std::borrow::Cow; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -40,15 +37,15 @@ pub enum WordToken<'a> { /// Braced alternatives Brace(Vec<&'a str>), /// An array literal - Array(Vec<&'a str>, Select<types::Str>), + Array(Vec<&'a str>, Option<&'a str>), /// A scalar variable - Variable(&'a str, Select<types::Str>), + Variable(&'a str, Option<&'a str>), /// An array or map-like variable - ArrayVariable(&'a str, bool, Select<types::Str>), + ArrayVariable(&'a str, bool, Option<&'a str>), /// A process that should expand to an array - ArrayProcess(&'a str, bool, Select<types::Str>), + ArrayProcess(&'a str, bool, Option<&'a str>), /// A process that expands to a scalar value - Process(&'a str, Select<types::Str>), + Process(&'a str, Option<&'a str>), /// A method on a scalar value StringMethod(StringMethod<'a>), /// A method on a array value @@ -59,16 +56,15 @@ pub enum WordToken<'a> { /// Iterate over the terminal tokens of the parsed text #[derive(Debug, PartialEq, Clone)] -pub struct WordIterator<'a, E: Expander> { - data: &'a str, - read: usize, - quotes: Quotes, - backsl: bool, - expanders: &'a E, - do_glob: bool, +pub struct WordIterator<'a> { + data: &'a str, + read: usize, + quotes: Quotes, + backsl: bool, + do_glob: bool, } -impl<'a, E: Expander + 'a> WordIterator<'a, E> { +impl<'a> WordIterator<'a> { fn arithmetic_expression<I: Iterator<Item = u8>>(&mut self, iter: &mut I) -> WordToken<'a> { let mut paren: i8 = 0; let start = self.read; @@ -138,7 +134,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { } /// Contains the grammar for parsing array expression syntax - fn array<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error> + fn array<I>(&mut self, iterator: &mut I) -> WordToken<'a> where I: Iterator<Item = u8>, { @@ -161,9 +157,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { return if let Some(&b'[') = self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::Array(elements, self.read_selection(iterator)?)) + WordToken::Array(elements, Some(self.read_selection(iterator))) } else { - Ok(WordToken::Array(elements, Select::All)) + WordToken::Array(elements, None) }; } else { level -= 1; @@ -218,7 +214,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { } /// Contains the logic for parsing array subshell syntax. - fn array_process<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error> + fn array_process<I>(&mut self, iterator: &mut I) -> WordToken<'a> where I: Iterator<Item = u8>, { @@ -243,17 +239,17 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { self.read += 1; return if let Some(&b'[') = self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::ArrayProcess( + WordToken::ArrayProcess( array_process_contents, self.quotes == Quotes::Double, - self.read_selection(iterator)?, - )) + Some(self.read_selection(iterator)), + ) } else { - Ok(WordToken::ArrayProcess( + WordToken::ArrayProcess( array_process_contents, self.quotes == Quotes::Double, - Select::All, - )) + None, + ) }; } else { level -= 1; @@ -269,7 +265,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { } /// Contains the logic for parsing subshell syntax. - fn process<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error> + fn process<I>(&mut self, iterator: &mut I) -> WordToken<'a> where I: Iterator<Item = u8>, { @@ -305,9 +301,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { self.read += 1; return if let Some(&b'[') = self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::Process(output, self.read_selection(iterator)?)) + WordToken::Process(output, Some(self.read_selection(iterator))) } else { - Ok(WordToken::Process(output, Select::All)) + WordToken::Process(output, None) }; } else { level -= 1; @@ -322,7 +318,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { panic!("ion: fatal error with syntax validation: unterminated process"); } - fn braced_array_variable<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error> + fn braced_array_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a> where I: Iterator<Item = u8>, { @@ -331,11 +327,11 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { while let Some(character) = iterator.next() { match character { b'[' => { - let result = Ok(WordToken::ArrayVariable( + let result = WordToken::ArrayVariable( &self.data[start..self.read], self.quotes == Quotes::Double, - self.read_selection(iterator)?, - )); + Some(self.read_selection(iterator)), + ); self.read += 1; if let Some(b'}') = iterator.next() { return result; @@ -348,33 +344,25 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { b'}' => { let output = &self.data[start..self.read]; self.read += 1; - return Ok(WordToken::ArrayVariable( - output, - self.quotes == Quotes::Double, - Select::All, - )); + return WordToken::ArrayVariable(output, self.quotes == Quotes::Double, None); } // Only alphanumerical and underscores are allowed in variable names 0..=47 | 58..=64 | 91..=94 | 96 | 123..=127 => { - return Ok(WordToken::ArrayVariable( + return WordToken::ArrayVariable( &self.data[start..self.read], self.quotes == Quotes::Double, - Select::All, - )); + None, + ); } _ => (), } self.read += 1; } - Ok(WordToken::ArrayVariable( - &self.data[start..], - self.quotes == Quotes::Double, - Select::All, - )) + WordToken::ArrayVariable(&self.data[start..], self.quotes == Quotes::Double, None) } /// Contains the logic for parsing array variable syntax - fn array_variable<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error> + fn array_variable<I>(&mut self, iterator: &mut I) -> WordToken<'a> where I: Iterator<Item = u8>, { @@ -408,25 +396,25 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::ArrayMethod( + WordToken::ArrayMethod( ArrayMethod::new( method, variable.trim(), Pattern::StringPattern(pattern), - self.read_selection(iterator)?, + Some(self.read_selection(iterator)), ), self.quotes == Quotes::Double, - )) + ) } else { - Ok(WordToken::ArrayMethod( + WordToken::ArrayMethod( ArrayMethod::new( method, variable.trim(), Pattern::StringPattern(pattern), - Select::All, + None, ), self.quotes == Quotes::Double, - )) + ) }; } self.read += 1; @@ -439,25 +427,25 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { return if let Some(&b'[') = self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::ArrayMethod( + WordToken::ArrayMethod( ArrayMethod::new( method, variable.trim(), Pattern::Whitespace, - self.read_selection(iterator)?, + Some(self.read_selection(iterator)), ), self.quotes == Quotes::Double, - )) + ) } else { - Ok(WordToken::ArrayMethod( + WordToken::ArrayMethod( ArrayMethod::new( method, variable.trim(), Pattern::Whitespace, - Select::All, + None, ), self.quotes == Quotes::Double, - )) + ) }; } b')' => depth -= 1, @@ -470,33 +458,29 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { panic!("ion: fatal error with syntax validation parsing: unterminated method"); } b'[' => { - return Ok(WordToken::ArrayVariable( + return WordToken::ArrayVariable( &self.data[start..self.read], self.quotes == Quotes::Double, - self.read_selection(iterator)?, - )); + Some(self.read_selection(iterator)), + ); } // Only alphanumerical and underscores are allowed in variable names 0..=47 | 58..=64 | 91..=94 | 96 | 123..=127 => { - return Ok(WordToken::ArrayVariable( + return WordToken::ArrayVariable( &self.data[start..self.read], self.quotes == Quotes::Double, - Select::All, - )); + None, + ); } _ => (), } self.read += 1; } - Ok(WordToken::ArrayVariable( - &self.data[start..], - self.quotes == Quotes::Double, - Select::All, - )) + WordToken::ArrayVariable(&self.data[start..], self.quotes == Quotes::Double, None) } - fn read_selection<I>(&mut self, iterator: &mut I) -> Result<Select<types::Str>, E::Error> + fn read_selection<I>(&mut self, iterator: &mut I) -> &'a str where I: Iterator<Item = u8>, { @@ -504,11 +488,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { let start = self.read; for character in iterator { if let b']' = character { - let value = self.expanders.expand_string(&self.data[start..self.read])?.join(" "); + let value = &self.data[start..self.read]; self.read += 1; - return value - .parse::<Select<types::Str>>() - .map_err(|_| super::Error::IndexParsingError(value)); + return value; } self.read += 1; } @@ -517,7 +499,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { } /// Contains the logic for parsing variable syntax - fn variable<I>(&mut self, iterator: &mut I) -> Result<WordToken<'a>, E::Error> + fn variable<I>(&mut self, iterator: &mut I) -> WordToken<'a> where I: Iterator<Item = u8>, { @@ -555,19 +537,19 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::StringMethod(StringMethod { + WordToken::StringMethod(StringMethod { method, variable: variable.trim(), pattern, - selection: self.read_selection(iterator)?, - })) + selection: Some(self.read_selection(iterator)), + }) } else { - Ok(WordToken::StringMethod(StringMethod { + WordToken::StringMethod(StringMethod { method, variable: variable.trim(), pattern, - selection: Select::All, - })) + selection: None, + }) }; } else if character == b'(' { depth += 1; @@ -585,19 +567,19 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { return if let Some(&b'[') = self.data.as_bytes().get(self.read) { let _ = iterator.next(); - Ok(WordToken::StringMethod(StringMethod { + WordToken::StringMethod(StringMethod { method, variable: variable.trim(), pattern: " ", - selection: self.read_selection(iterator)?, - })) + selection: Some(self.read_selection(iterator)), + }) } else { - Ok(WordToken::StringMethod(StringMethod { + WordToken::StringMethod(StringMethod { method, variable: variable.trim(), pattern: " ", - selection: Select::All, - })) + selection: None, + }) }; } b')' => depth -= 1, @@ -614,9 +596,9 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { let variable = &self.data[start..self.read]; return if character == b'[' { - Ok(WordToken::Variable(variable, self.read_selection(iterator)?)) + WordToken::Variable(variable, Some(self.read_selection(iterator))) } else { - Ok(WordToken::Variable(variable, Select::All)) + WordToken::Variable(variable, None) }; } _ => (), @@ -624,7 +606,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { self.read += 1; } - Ok(WordToken::Variable(&self.data[start..], Select::All)) + WordToken::Variable(&self.data[start..], None) } // Contains the logic for parsing braced variables @@ -637,7 +619,7 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { if character == b'}' { let output = &self.data[start..self.read]; self.read += 1; - return WordToken::Variable(output, Select::All); + return WordToken::Variable(output, None); } self.read += 1; } @@ -664,16 +646,13 @@ impl<'a, E: Expander + 'a> WordIterator<'a, E> { } /// Creates a new iterator with a given expander - pub fn new(data: &'a str, expanders: &'a E, do_glob: bool) -> WordIterator<'a, E> { - WordIterator { data, backsl: false, read: 0, quotes: Quotes::None, expanders, do_glob } + pub fn new(data: &'a str, do_glob: bool) -> WordIterator<'a> { + WordIterator { data, backsl: false, read: 0, quotes: Quotes::None, do_glob } } } -impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> -where - <E as Expander>::Error: 'static, -{ - type Item = Result<WordToken<'a>, E::Error>; +impl<'a> Iterator for WordIterator<'a> { + type Item = WordToken<'a>; fn next(&mut self) -> Option<Self::Item> { if self.read == self.data.len() { @@ -721,7 +700,7 @@ where break; } b' ' if self.quotes == Quotes::None => { - return Some(Ok(self.whitespaces(&mut iterator))); + return Some(self.whitespaces(&mut iterator)); } b'~' if self.quotes == Quotes::None => { tilde = true; @@ -730,7 +709,7 @@ where } b'{' if self.quotes == Quotes::None => { self.read += 1; - return Some(Ok(self.braces(&mut iterator))); + return Some(self.braces(&mut iterator)); } b'[' if self.quotes == Quotes::None => { if self.glob_check(&mut iterator) { @@ -752,7 +731,7 @@ where Some(b' ') | None => { self.read += 1; let output = &self.data[start..self.read]; - Some(Ok(WordToken::Normal(output.into(), glob, tilde))) + Some(WordToken::Normal(output.into(), glob, tilde)) } _ => { self.read += 1; @@ -768,19 +747,19 @@ where // Pop the incoming left paren let _ = iterator.next(); self.read += 1; - Some(Ok(self.arithmetic_expression(&mut iterator))) + Some(self.arithmetic_expression(&mut iterator)) } else { Some(self.process(&mut iterator)) } } Some(b'{') => { self.read += 2; - Some(Ok(self.braced_variable(&mut iterator))) + Some(self.braced_variable(&mut iterator)) } Some(b' ') | None => { self.read += 1; let output = &self.data[start..self.read]; - Some(Ok(WordToken::Normal(output.into(), glob, tilde))) + Some(WordToken::Normal(output.into(), glob, tilde)) } _ => { self.read += 1; @@ -809,7 +788,7 @@ where if self.quotes == Quotes::Double { let _ = iterator.next(); self.read += 1; - return Some(Ok(WordToken::Normal( + return Some(WordToken::Normal( if next.map_or(true, |c| [b'$', b'@', b'\\', b'"'].contains(&c)) { unescape(&self.data[start..self.read]) } else { @@ -817,7 +796,7 @@ where }, glob, tilde, - ))); + )); } } b'\'' if self.quotes != Quotes::Double => { @@ -828,7 +807,7 @@ where } let output = &self.data[start..self.read]; self.read += 1; - return Some(Ok(WordToken::Normal(output.into(), glob, tilde))); + return Some(WordToken::Normal(output.into(), glob, tilde)); } b'"' if self.quotes != Quotes::Single => { if self.quotes == Quotes::Double { @@ -838,39 +817,39 @@ where } let output = &self.data[start..self.read]; self.read += 1; - return Some(Ok(WordToken::Normal(output.into(), glob, tilde))); + return Some(WordToken::Normal(output.into(), glob, tilde)); } b' ' | b'{' if self.quotes == Quotes::None => { - return Some(Ok(WordToken::Normal( + return Some(WordToken::Normal( unescape(&self.data[start..self.read]), glob, tilde, - ))); + )); } b'$' | b'@' if self.quotes != Quotes::Single => { if let Some(&character) = self.data.as_bytes().get(self.read) { if character == b' ' { self.read += 1; let output = &self.data[start..self.read]; - return Some(Ok(WordToken::Normal(output.into(), glob, tilde))); + return Some(WordToken::Normal(output.into(), glob, tilde)); } } if self.read == start { return self.next(); } else { let output = &self.data[start..self.read]; - return Some(Ok(WordToken::Normal(unescape(output), glob, tilde))); + return Some(WordToken::Normal(unescape(output), glob, tilde)); }; } b'[' if self.quotes == Quotes::None => { if self.glob_check(&mut iterator) { glob = self.do_glob; } else { - return Some(Ok(WordToken::Normal( + return Some(WordToken::Normal( self.data[start..self.read].into(), glob, tilde, - ))); + )); } } b'*' | b'?' if self.quotes != Quotes::Single => { @@ -884,7 +863,7 @@ where if start == self.read { None } else { - Some(Ok(WordToken::Normal(unescape(&self.data[start..]), glob, tilde))) + Some(WordToken::Normal(unescape(&self.data[start..]), glob, tilde)) } } } diff --git a/src/lib/expansion/words/tests.rs b/src/lib/expansion/words/tests.rs index d4e43284..633c5190 100644 --- a/src/lib/expansion/words/tests.rs +++ b/src/lib/expansion/words/tests.rs @@ -1,13 +1,9 @@ use super::*; -use crate::{ - expansion::test::DummyExpander, - ranges::{Index, Range}, -}; +use crate::expansion::test::DummyExpander; -fn compare(input: &str, expected: Vec<WordToken<'_>>) { +fn compare(input: &str, expected: &[WordToken<'_>]) { let mut correct = 0; - for (actual, expected) in WordIterator::new(input, &DummyExpander, true).zip(expected.iter()) { - let actual = actual.unwrap(); + for (actual, expected) in WordIterator::new(input, true).zip(expected.iter()) { assert_eq!(actual, *expected, "{:?} != {:?}", actual, expected); correct += 1; } @@ -17,19 +13,19 @@ fn compare(input: &str, expected: Vec<WordToken<'_>>) { #[test] fn string_method() { let input = "$join(array 'pattern') $join(array 'pattern')"; - let expected = vec![ + let expected = &[ WordToken::StringMethod(StringMethod { method: "join", variable: "array", pattern: "'pattern'", - selection: Select::All, + selection: None, }), WordToken::Whitespace(" "), WordToken::StringMethod(StringMethod { method: "join", variable: "array", pattern: "'pattern'", - selection: Select::All, + selection: None, }), ]; compare(input, expected); @@ -38,7 +34,7 @@ fn string_method() { #[test] fn escape_with_backslash() { let input = r#"\$FOO\$BAR \$FOO"#; - let expected = vec![ + let expected = &[ WordToken::Normal("$FOO$BAR".into(), false, false), WordToken::Whitespace(" "), WordToken::Normal("$FOO".into(), false, false), @@ -51,10 +47,10 @@ fn array_expressions() { let input = "[ one two [three four]] [[one two] three four][0]"; let first = vec!["one", "two", "[three four]"]; let second = vec!["[one two]", "three", "four"]; - let expected = vec![ - WordToken::Array(first, Select::All), + let expected = &[ + WordToken::Array(first, None), WordToken::Whitespace(" "), - WordToken::Array(second, Select::Index(Index::new(0))), + WordToken::Array(second, Some("0")), ]; compare(input, expected); } @@ -62,12 +58,12 @@ fn array_expressions() { #[test] fn array_variables() { let input = "@array @array[0] @{array[1..]}"; - let expected = vec![ - WordToken::ArrayVariable("array", false, Select::All), + let expected = &[ + WordToken::ArrayVariable("array", false, None), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Index(Index::new(0))), + WordToken::ArrayVariable("array", false, Some("0")), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Range(Range::from(Index::new(1)))), + WordToken::ArrayVariable("array", false, Some("1..")), ]; compare(input, expected); } @@ -75,10 +71,10 @@ fn array_variables() { #[test] fn array_processes() { let input = "@(echo one two three) @(echo one two three)[0]"; - let expected = vec![ - WordToken::ArrayProcess("echo one two three", false, Select::All), + let expected = &[ + WordToken::ArrayProcess("echo one two three", false, None), WordToken::Whitespace(" "), - WordToken::ArrayProcess("echo one two three", false, Select::Index(Index::new(0))), + WordToken::ArrayProcess("echo one two three", false, Some("0")), ]; compare(input, expected); } @@ -87,13 +83,10 @@ fn array_processes() { fn array_process_within_string_process() { compare( "echo $(let free=[@(free -h)]; echo @free[6]@free[8]/@free[7])", - vec![ + &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), - WordToken::Process( - "let free=[@(free -h)]; echo @free[6]@free[8]/@free[7]", - Select::All, - ), + WordToken::Process("let free=[@(free -h)]; echo @free[6]@free[8]/@free[7]", None), ], ) } @@ -101,24 +94,16 @@ fn array_process_within_string_process() { #[test] fn indexes() { let input = "@array[0..3] @array[0...3] @array[abc] @array[..3] @array[3..]"; - let expected = vec![ - WordToken::ArrayVariable( - "array", - false, - Select::Range(Range::exclusive(Index::new(0), Index::new(3))), - ), + let expected = &[ + WordToken::ArrayVariable("array", false, Some("0..3")), WordToken::Whitespace(" "), - WordToken::ArrayVariable( - "array", - false, - Select::Range(Range::inclusive(Index::new(0), Index::new(3))), - ), + WordToken::ArrayVariable("array", false, Some("0...3")), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Key("abc".into())), + WordToken::ArrayVariable("array", false, Some("abc")), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Range(Range::to(Index::new(3)))), + WordToken::ArrayVariable("array", false, Some("..3")), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Range(Range::from(Index::new(3)))), + WordToken::ArrayVariable("array", false, Some("3..")), ]; compare(input, expected); } @@ -126,12 +111,12 @@ fn indexes() { #[test] fn string_keys() { let input = "@array['key'] @array[key] @array[]"; - let expected = vec![ - WordToken::ArrayVariable("array", false, Select::Key("key".into())), + let expected = &[ + WordToken::ArrayVariable("array", false, Some("'key'")), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Key("key".into())), + WordToken::ArrayVariable("array", false, Some("key")), WordToken::Whitespace(" "), - WordToken::ArrayVariable("array", false, Select::Key("".into())), + WordToken::ArrayVariable("array", false, Some("")), ]; compare(input, expected); } @@ -139,12 +124,12 @@ fn string_keys() { #[test] fn nested_processes() { let input = "echo $(echo $(echo one)) $(echo one $(echo two) three)"; - let expected = vec![ + let expected = &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), - WordToken::Process("echo $(echo one)", Select::All), + WordToken::Process("echo $(echo one)", None), WordToken::Whitespace(" "), - WordToken::Process("echo one $(echo two) three", Select::All), + WordToken::Process("echo one $(echo two) three", None), ]; compare(input, expected); } @@ -152,18 +137,18 @@ fn nested_processes() { #[test] fn words_process_with_quotes() { let input = "echo $(git branch | rg '[*]' | awk '{print $2}')"; - let expected = vec![ + let expected = &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), - WordToken::Process("git branch | rg '[*]' | awk '{print $2}'", Select::All), + WordToken::Process("git branch | rg '[*]' | awk '{print $2}'", None), ]; compare(input, expected); let input = "echo $(git branch | rg \"[*]\" | awk '{print $2}')"; - let expected = vec![ + let expected = &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), - WordToken::Process("git branch | rg \"[*]\" | awk '{print $2}'", Select::All), + WordToken::Process("git branch | rg \"[*]\" | awk '{print $2}'", None), ]; compare(input, expected); } @@ -171,21 +156,21 @@ fn words_process_with_quotes() { #[test] fn test_words() { let input = "echo $ABC \"${ABC}\" one{$ABC,$ABC} ~ $(echo foo) \"$(seq 1 100)\""; - let expected = vec![ + let expected = &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), - WordToken::Variable("ABC", Select::All), + WordToken::Variable("ABC", None), WordToken::Whitespace(" "), - WordToken::Variable("ABC", Select::All), + WordToken::Variable("ABC", None), WordToken::Whitespace(" "), WordToken::Normal("one".into(), false, false), WordToken::Brace(vec!["$ABC", "$ABC"]), WordToken::Whitespace(" "), WordToken::Normal("~".into(), false, true), WordToken::Whitespace(" "), - WordToken::Process("echo foo", Select::All), + WordToken::Process("echo foo", None), WordToken::Whitespace(" "), - WordToken::Process("seq 1 100", Select::All), + WordToken::Process("seq 1 100", None), ]; compare(input, expected); } @@ -193,7 +178,7 @@ fn test_words() { #[test] fn test_multiple_escapes() { let input = "foo\\(\\) bar\\(\\)"; - let expected = vec![ + let expected = &[ WordToken::Normal("foo()".into(), false, false), WordToken::Whitespace(" "), WordToken::Normal("bar()".into(), false, false), @@ -204,7 +189,7 @@ fn test_multiple_escapes() { #[test] fn test_arithmetic() { let input = "echo $((foo bar baz bing 3 * 2))"; - let expected = vec![ + let expected = &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), WordToken::Arithmetic("foo bar baz bing 3 * 2"), @@ -215,7 +200,7 @@ fn test_arithmetic() { #[test] fn test_globbing() { let input = "barbaz* bingcrosb*"; - let expected = vec![ + let expected = &[ WordToken::Normal("barbaz*".into(), true, false), WordToken::Whitespace(" "), WordToken::Normal("bingcrosb*".into(), true, false), @@ -226,7 +211,7 @@ fn test_globbing() { #[test] fn test_empty_strings() { let input = "rename '' 0 a \"\""; - let expected = vec![ + let expected = &[ WordToken::Normal("rename".into(), false, false), WordToken::Whitespace(" "), WordToken::Normal("".into(), false, false), @@ -243,7 +228,7 @@ fn test_empty_strings() { #[test] fn test_braces() { let input = "echo {c[a,b],d}"; - let expected = vec![ + let expected = &[ WordToken::Normal("echo".into(), false, false), WordToken::Whitespace(" "), WordToken::Brace(vec!["c[a,b]", "d"]), @@ -253,20 +238,13 @@ fn test_braces() { #[test] fn array_methods() { - let method = ArrayMethod::new( - "graphemes", - "pkmn1", - Pattern::Whitespace, - Select::Index(Index::Forward(3)), - ); + let method = ArrayMethod::new("graphemes", "pkmn1", Pattern::Whitespace, Some("3")); let expected = args!["é"]; assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), expected); - let method = - ArrayMethod::new("chars", "pkmn2", Pattern::Whitespace, Select::Index(Index::Forward(3))); + let method = ArrayMethod::new("chars", "pkmn2", Pattern::Whitespace, Some("3")); let expected = args!["e"]; assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), expected); - let method = - ArrayMethod::new("bytes", "pkmn2", Pattern::Whitespace, Select::Index(Index::Forward(1))); + let method = ArrayMethod::new("bytes", "pkmn2", Pattern::Whitespace, Some("1")); let expected = args!["111"]; assert_eq!(method.handle_as_array(&DummyExpander).unwrap(), expected); } diff --git a/src/lib/shell/shell_expand.rs b/src/lib/shell/shell_expand.rs index b85b59f6..ad39f5b6 100644 --- a/src/lib/shell/shell_expand.rs +++ b/src/lib/shell/shell_expand.rs @@ -152,30 +152,26 @@ impl<'a, 'b> Expander for Shell<'b> { } } - fn map_keys(&self, name: &str, sel: &Select<types::Str>) -> Result<types::Args, Self::Error> { + fn map_keys(&self, name: &str) -> Result<types::Args, Self::Error> { match self.variables.get(name) { Some(&Value::HashMap(ref map)) => { - Self::select(map.keys().map(|x| format!("{}", x).into()), sel, map.len()) - .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into())) + Ok(map.keys().map(|x| x.to_string().into()).collect()) } Some(&Value::BTreeMap(ref map)) => { - Self::select(map.keys().map(|x| format!("{}", x).into()), sel, map.len()) - .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into())) + Ok(map.keys().map(|x| x.to_string().into()).collect()) } Some(_) => Err(Error::NotAMap(name.into())), None => Err(Error::VarNotFound), } } - fn map_values(&self, name: &str, sel: &Select<types::Str>) -> Result<types::Args, Self::Error> { + fn map_values(&self, name: &str) -> Result<types::Args, Self::Error> { match self.variables.get(name) { Some(&Value::HashMap(ref map)) => { - Self::select(map.values().map(|x| format!("{}", x).into()), sel, map.len()) - .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into())) + Ok(map.values().map(|x| x.to_string().into()).collect()) } Some(&Value::BTreeMap(ref map)) => { - Self::select(map.values().map(|x| format!("{}", x).into()), sel, map.len()) - .ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into())) + Ok(map.values().map(|x| x.to_string().into()).collect()) } Some(_) => Err(Error::NotAMap(name.into())), None => Err(Error::VarNotFound), diff --git a/src/lib/shell/variables.rs b/src/lib/shell/variables.rs index 2c69cb70..fa5692c6 100644 --- a/src/lib/shell/variables.rs +++ b/src/lib/shell/variables.rs @@ -312,19 +312,11 @@ pub(crate) mod tests { fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) } - fn map_keys( - &self, - _name: &str, - _select: &Select<types::Str>, - ) -> Result<types::Args, Self::Error> { + fn map_keys(&self, _name: &str) -> Result<types::Args, Self::Error> { Err(expansion::Error::VarNotFound) } - fn map_values( - &self, - _name: &str, - _select: &Select<types::Str>, - ) -> Result<types::Args, Self::Error> { + fn map_values(&self, _name: &str) -> Result<types::Args, Self::Error> { Err(expansion::Error::VarNotFound) } } -- GitLab