diff --git a/src/cli.rs b/src/cli.rs index 0ec35c094f..5efd47ea88 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -60,6 +60,7 @@ pub async fn cli() -> Result<(), Box> { ("skip", Arc::new(skip::skip)), ("first", Arc::new(take::take)), ("select", Arc::new(select::select)), + ("size", Arc::new(size::size)), ("split", Arc::new(split::split)), ("reject", Arc::new(reject::reject)), ("to-array", Arc::new(to_array::to_array)), diff --git a/src/commands.rs b/src/commands.rs index b48af5b02f..6dcc6a83e6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -6,6 +6,7 @@ crate mod ls; crate mod ps; crate mod reject; crate mod select; +crate mod size; crate mod skip; crate mod sort_by; crate mod split; diff --git a/src/commands/size.rs b/src/commands/size.rs new file mode 100644 index 0000000000..aea5995974 --- /dev/null +++ b/src/commands/size.rs @@ -0,0 +1,84 @@ +use crate::errors::ShellError; +use crate::object::dict::Dictionary; +use crate::object::Value; +use crate::prelude::*; +use std::fs::File; +use std::io::prelude::*; + +pub fn size(args: CommandArgs) -> Result { + if args.args.is_empty() { + return Err(ShellError::string("size requires at least one file")); + } + let cwd = args.env.lock().unwrap().cwd().to_path_buf(); + + let mut contents = String::new(); + let mut total_lines = 0; + let mut total_words = 0; + let mut total_chars = 0; + let mut total_bytes = 0; + + let mut list = VecDeque::new(); + for name in args.args { + let name = name.as_string()?; + let path = cwd.join(&name); + let mut file = File::open(path)?; + + file.read_to_string(&mut contents)?; + let (lines, words, chars, bytes) = count(&contents); + + total_lines += lines; + total_words += words; + total_chars += chars; + total_bytes += bytes; + + list.push_back(dict(&name, lines, words, chars, bytes)); + contents.clear(); + } + list.push_back(dict( + &"total".to_string(), + total_lines, + total_words, + total_chars, + total_bytes, + )); + + Ok(list.boxed()) +} + +fn count(contents: &str) -> (i64, i64, i64, i64) { + let mut lines: i64 = 0; + let mut words: i64 = 0; + let mut chars: i64 = 0; + let mut end_of_word = true; + + for c in contents.chars() { + chars += 1; + + match c { + '\n' => { + lines += 1; + end_of_word = true; + } + ' ' => end_of_word = true, + _ => { + if end_of_word { + words += 1; + } + end_of_word = false; + } + } + } + + (lines, words, chars, contents.len() as i64) +} + +fn dict(name: &str, lines: i64, words: i64, chars: i64, bytes: i64) -> ReturnValue { + let mut dict = Dictionary::default(); + dict.add("name", Value::string(name.to_owned())); + dict.add("lines", Value::int(lines)); + dict.add("words", Value::int(words)); + dict.add("chars", Value::int(chars)); + dict.add("max length", Value::int(bytes)); + + ReturnValue::Value(Value::Object(dict)) +} diff --git a/src/shell/completer.rs b/src/shell/completer.rs index f3912ca672..7afceb3543 100644 --- a/src/shell/completer.rs +++ b/src/shell/completer.rs @@ -17,7 +17,7 @@ impl Completer for NuCompleter { ) -> rustyline::Result<(usize, Vec)> { let commands = [ "ps", "ls", "cd", "view", "skip", "take", "select", "reject", "to-array", "where", - "sort-by", + "sort-by", "size", ]; let mut completions = self.file_completer.complete(line, pos, context)?.1;