diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs index 151f14921c8a914c899dc07bb50fa0e7ea1a2e4b..87a4465d064441a6ef08d0594d9ac11641aa5872 100644 --- a/src/lib/builtins/mod.rs +++ b/src/lib/builtins/mod.rs @@ -228,7 +228,7 @@ fn builtin_dirs(args: &[small::String], shell: &mut Shell) -> i32 { return SUCCESS; } - shell.directory_stack.dirs(args) + shell.directory_stack.dirs(args.iter()) } fn builtin_pushd(args: &[small::String], shell: &mut Shell) -> i32 { diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index b1d7c8b08296c7b606b2f7875474befcd1321688..bb912983610c37b3f098a1b3204651db86b71291 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -8,7 +8,7 @@ pub(crate) mod statement; pub use self::quotes::Terminator; pub(crate) use self::{ loops::ForValueExpression, - shell_expand::{expand_string, Expander, MapKeyIter, MapValueIter, Select}, + shell_expand::{expand_string, Expander, Select}, statement::{parse_and_validate, StatementSplitter}, }; diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs index 97a177469494bee37612d1ea03e756f6c53e791a..d08999845e964a79b1c876b481fab217b31e11fc 100644 --- a/src/lib/parser/shell_expand/mod.rs +++ b/src/lib/parser/shell_expand/mod.rs @@ -26,9 +26,6 @@ pub(crate) fn is_expression(s: &str) -> bool { || s.starts_with('\'') } -pub type MapKeyIter<'a> = Box<dyn Iterator<Item = &'a types::Str> + 'a>; -pub type MapValueIter<'a> = Box<dyn Iterator<Item = types::Str> + 'a>; - // TODO: Make array expansions iterators instead of arrays. // TODO: Use Cow<'a, types::Str> for hashmap values. /// Trait representing different elements of string expansion @@ -42,9 +39,9 @@ pub(crate) trait Expander: Sized { /// Expand a subshell expression. fn command(&self, _command: &str) -> Option<types::Str> { None } /// Iterating upon key-value maps. - fn map_keys<'a>(&'a self, _name: &str, _select: Select) -> Option<MapKeyIter> { None } + fn map_keys<'a>(&'a self, _name: &str, _select: Select) -> Option<Array> { None } /// Iterating upon key-value maps. - fn map_values<'a>(&'a self, _name: &str, _select: Select) -> Option<MapValueIter> { None } + fn map_values<'a>(&'a self, _name: &str, _select: Select) -> Option<Array> { None } /// Get a string that exists in the shell. fn get_string(&self, value: &str) -> Value { Value::Str(types::Str::from(expand_string(value, self, false).join(" "))) diff --git a/src/lib/parser/shell_expand/words/methods/arrays.rs b/src/lib/parser/shell_expand/words/methods/arrays.rs index 6eb54cc83a2707b5e2a069594968e8dac54c2b08..97c9484270505f9efa7e2ae5eabf95ffeed89694 100644 --- a/src/lib/parser/shell_expand/words/methods/arrays.rs +++ b/src/lib/parser/shell_expand/words/methods/arrays.rs @@ -30,8 +30,7 @@ impl<'a> ArrayMethod<'a> { } fn lines<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> { - let variable = self.resolve_var(expand_func); - Ok(variable.lines().map(types::Str::from).collect()) + Ok(self.resolve_var(expand_func).lines().map(types::Str::from).collect()) } fn chars<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> { @@ -45,7 +44,7 @@ impl<'a> ArrayMethod<'a> { fn bytes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> { let variable = self.resolve_var(expand_func); - let len = variable.as_bytes().len(); + let len = variable.len(); Ok(variable .bytes() .map(|b| types::Str::from(b.to_string())) @@ -53,17 +52,11 @@ impl<'a> ArrayMethod<'a> { } fn map_keys<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Array, &'static str> { - expand_func - .map_keys(self.variable, self.selection.clone()) - .ok_or("no map found") - .map(|x| x.cloned().collect()) + expand_func.map_keys(self.variable, self.selection.clone()).ok_or("no map found") } fn map_values<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Array, &'static str> { - expand_func - .map_values(self.variable, self.selection.clone()) - .ok_or("no map found") - .map(|x| x.collect()) + expand_func.map_values(self.variable, self.selection.clone()).ok_or("no map found") } fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> { diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs index c3abfff7853beaacfd1e42772de3e1114e2ad5ab..d63317e61b55c070f6091fc311d6ea746a05cd42 100644 --- a/src/lib/shell/directory_stack.rs +++ b/src/lib/shell/directory_stack.rs @@ -95,72 +95,67 @@ impl DirectoryStack { pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) } - pub(crate) fn dirs<I: IntoIterator>(&mut self, args: I) -> i32 - where - I::Item: AsRef<str>, - { - const CLEAR: u8 = 1; // -c - const ABS_PATHNAMES: u8 = 2; // -l - const MULTILINE: u8 = 4; // -p | -v - const INDEX: u8 = 8; // -v + pub(crate) fn dirs<'a, I: Iterator<Item = &'a T>, T: 'a + AsRef<str>>( + &mut self, + args: I, + ) -> i32 { + let mut clear = false; // -c + let mut abs_pathnames = false; // -l + let mut multiline = false; // -p | -v + let mut index = false; // -v - let mut dirs_args: u8 = 0; - let mut num_arg: Option<usize> = None; + let mut num_arg = None; - for arg in args.into_iter().skip(1) { - let arg = arg.as_ref(); + for arg in args.skip(1).map(AsRef::as_ref) { match arg { - "-c" => dirs_args |= CLEAR, - "-l" => dirs_args |= ABS_PATHNAMES, - "-p" => dirs_args |= MULTILINE, - "-v" => dirs_args |= INDEX | MULTILINE, - arg => { - num_arg = match parse_numeric_arg(arg) { - Some((true, num)) => Some(num), - Some((false, num)) if self.dirs.len() > num => { - Some(self.dirs.len() - num - 1) - } - _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid - * argument\n", arg))) */ - }; + "-c" => clear = true, + "-l" => abs_pathnames = true, + "-p" => multiline = true, + "-v" => { + index = true; + multiline = true; } + arg => num_arg = Some(arg), } } - if dirs_args & CLEAR > 0 { + if clear { self.dirs.truncate(1); } - let mapper: fn((usize, &PathBuf)) -> Cow<str> = - match (dirs_args & ABS_PATHNAMES > 0, dirs_args & INDEX > 0) { - // ABS, INDEX - (true, true) => |(num, x)| Cow::Owned(format!(" {} {}", num, try_abs_path(x))), - (true, false) => |(_, x)| try_abs_path(x), - (false, true) => { - |(num, x)| Cow::Owned(format!(" {} {}", num, x.to_string_lossy())) - } - (false, false) => |(_, x)| x.to_string_lossy(), - }; + let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (abs_pathnames, index) { + // ABS, INDEX + (true, true) => |(num, x)| Cow::Owned(format!(" {} {}", num, try_abs_path(x))), + (true, false) => |(_, x)| try_abs_path(x), + (false, true) => |(num, x)| Cow::Owned(format!(" {} {}", num, x.to_string_lossy())), + (false, false) => |(_, x)| x.to_string_lossy(), + }; let mut iter = self.dirs.iter().enumerate().map(mapper); - if let Some(num) = num_arg { - match iter.nth(num) { - Some(x) => println!("{}", x), - None => return FAILURE, + if let Some(arg) = num_arg { + let num = match parse_numeric_arg(arg) { + Some((true, num)) => num, + Some((false, num)) if self.dirs.len() > num => self.dirs.len() - num - 1, + _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid + * argument\n", arg))) */ }; + match iter.nth(num) { + Some(x) => { + println!("{}", x); + SUCCESS + } + None => FAILURE, + } } else { let folder: fn(String, Cow<str>) -> String = - if dirs_args & MULTILINE > 0 { |x, y| x + "\n" + &y } else { |x, y| x + " " + &y }; + if multiline { |x, y| x + "\n" + &y } else { |x, y| x + " " + &y }; - let first = match iter.next() { - Some(x) => x.to_string(), - None => return SUCCESS, - }; - - println!("{}", iter.fold(first, folder)); + if let Some(x) = iter.next() { + println!("{}", iter.fold(x.to_string(), folder)); + } + SUCCESS } - SUCCESS } fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) { @@ -194,27 +189,20 @@ impl DirectoryStack { } fn get_previous_dir(&self) -> Option<String> { - env::var("OLDPWD").ok().and_then(|pwd| { - if pwd.is_empty() || pwd == "?" { - None - } else { - Some(pwd) - } - }) + env::var("OLDPWD").ok().filter(|pwd| !pwd.is_empty() && pwd != &"?") } fn switch_to_previous_directory( &mut self, variables: &Variables, ) -> Result<(), Cow<'static, str>> { - match self.get_previous_dir() { - Some(prev) => { + self.get_previous_dir() + .ok_or(Cow::Borrowed("ion: no previous directory to switch to")) + .and_then(|prev| { self.dirs.remove(0); println!("{}", prev); self.change_and_push_dir(&prev, variables) - } - None => Err(Cow::Borrowed("ion: no previous directory to switch to")), - } + }) } fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> { @@ -260,13 +248,13 @@ impl DirectoryStack { } } - pub(crate) fn pushd<I: IntoIterator>( + pub(crate) fn pushd<T>( &mut self, - args: I, + args: &[T], variables: &mut Variables, ) -> Result<(), Cow<'static, str>> where - I::Item: AsRef<str>, + T: AsRef<str>, { enum Action { Switch, // <no arguments> @@ -276,10 +264,9 @@ impl DirectoryStack { } let mut keep_front = false; // whether the -n option is present - let mut action: Action = Action::Switch; + let mut action = Action::Switch; - for arg in args.into_iter().skip(1) { - let arg = arg.as_ref(); + for arg in args.iter().skip(1).map(AsRef::as_ref) { if arg == "-n" { keep_front = true; } else if let Action::Switch = action { @@ -331,64 +318,50 @@ impl DirectoryStack { /// Attempts to set the current directory to the directory stack's previous directory, /// and then removes the front directory from the stack. - pub(crate) fn popd<I: IntoIterator>(&mut self, args: I) -> Result<(), Cow<'static, str>> - where - I::Item: AsRef<str>, - { + pub(crate) fn popd<T: AsRef<str>>(&mut self, args: &[T]) -> Result<(), Cow<'static, str>> { + let len = self.dirs.len(); + if len <= 1 { + return Err(Cow::Borrowed("ion: popd: directory stack empty")); + } + let mut keep_front = false; // whether the -n option is present - let mut count_from_front = true; // <=> input number is positive - let mut num: usize = 0; + let mut index: usize = 0; - for arg in args.into_iter().skip(1) { - let arg = arg.as_ref(); + for arg in args.iter().skip(1).map(AsRef::as_ref) { if arg == "-n" { keep_front = true; } else { - match parse_numeric_arg(arg) { - Some((x, y)) => { - count_from_front = x; - num = y; - } - None => { - return Err(Cow::Owned(format!("ion: popd: {}: invalid argument", arg))) - } + let (count_from_front, num) = parse_numeric_arg(arg) + .ok_or(Cow::Owned(format!("ion: popd: {}: invalid argument", arg)))?; + + index = if count_from_front { + // <=> input number is positive + num + } else { + (len - 1).checked_sub(num).ok_or_else(|| { + Cow::Owned( + "ion: popd: negative directory stack index out of range".to_owned(), + ) + })? }; } } - let len: usize = self.dirs.len(); - if len <= 1 { - return Err(Cow::Borrowed("ion: popd: directory stack empty")); - } - - let mut index: usize = if count_from_front { - num - } else { - (len - 1).checked_sub(num).ok_or_else(|| { - Cow::Owned("ion: popd: negative directory stack index out of range".to_owned()) - })? - }; - // apply -n if index == 0 && keep_front { index = 1; - } - - // change to new directory, return if not possible - if index == 0 { + } else if index == 0 { + // change to new directory, return if not possible self.set_current_dir_by_index(1, "popd")?; } // pop element - if self.dirs.remove(index).is_none() { - return Err(Cow::Owned(format!( - "ion: popd: {}: directory stack index out of range", - index - ))); + if self.dirs.remove(index).is_some() { + self.print_dirs(); + Ok(()) + } else { + Err(Cow::Owned(format!("ion: popd: {}: directory stack index out of range", index))) } - - self.print_dirs(); - Ok(()) } /// This function will take a map of variables as input and attempt to parse the value of @@ -408,14 +381,13 @@ impl DirectoryStack { Ok(curr_dir) => { env::set_var("PWD", curr_dir.to_str().unwrap_or_else(|| "?")); dirs.push_front(curr_dir); - DirectoryStack { dirs } } Err(_) => { eprintln!("ion: failed to get current directory when building directory stack"); env::set_var("PWD", "?"); - DirectoryStack { dirs } } } + DirectoryStack { dirs } } } diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index a3a3ad5ebb98c08c3132e777f480ad131cc59dea..1e14871e509e28926042f395945d8544972e1cc7 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -53,10 +53,7 @@ use self::{ use crate::{ builtins::{BuiltinMap, BUILTINS}, lexers::{Key, Operator, Primitive}, - parser::{ - assignments::value_check, pipelines::Pipeline, Expander, MapKeyIter, MapValueIter, Select, - Terminator, - }, + parser::{assignments::value_check, pipelines::Pipeline, Expander, Select, Terminator}, sys, types::{self, Array}, }; @@ -678,36 +675,31 @@ impl<'a> Expander for Shell { None } - fn map_keys<'b>(&'b self, name: &str, select: Select) -> Option<MapKeyIter<'b>> { + fn map_keys(&self, name: &str, select: Select) -> Option<Array> { let nvalues; - let map: Box<dyn Iterator<Item = &'b types::Str>> = match self.variables.get_ref(name) { + let map: Box<dyn Iterator<Item = types::Str>> = match self.variables.get_ref(name) { Some(&Value::HashMap(ref map)) => { nvalues = map.len(); - Box::new(map.keys()) + Box::new(map.keys().cloned()) } Some(&Value::BTreeMap(ref map)) => { nvalues = map.len(); - Box::new(map.keys()) + Box::new(map.keys().cloned()) } _ => return None, }; match select { - Select::All => return Some(map), - Select::Range(range) => { - if let Some((start, length)) = range.bounds(nvalues) { - if nvalues > start { - return Some(Box::new(map.skip(start).take(length))); - } - } - } - _ => (), + Select::All => Some(map.collect()), + Select::Range(range) => range + .bounds(nvalues) + .filter(|&(start, _)| nvalues > start) + .map(|(start, length)| map.skip(start).take(length).collect()), + _ => None, } - - None } - fn map_values<'b>(&'b self, name: &str, select: Select) -> Option<MapValueIter<'b>> { + fn map_values(&self, name: &str, select: Select) -> Option<Array> { let nvalues; let map: Box<dyn Iterator<Item = types::Str>> = match self.variables.get_ref(name) { Some(&Value::HashMap(ref map)) => { @@ -722,18 +714,13 @@ impl<'a> Expander for Shell { }; match select { - Select::All => return Some(map), - Select::Range(range) => { - if let Some((start, length)) = range.bounds(nvalues) { - if nvalues > start { - return Some(Box::new(map.skip(start).take(length))); - } - } - } - _ => (), + Select::All => Some(map.collect()), + Select::Range(range) => range + .bounds(nvalues) + .filter(|&(start, _)| nvalues > start) + .map(|(start, length)| map.skip(start).take(length).collect()), + _ => None, } - - None } fn tilde(&self, input: &str) -> Option<String> {