diff --git a/src/filters.rs b/src/filters.rs index 9fab6bd..c286c25 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -74,6 +74,12 @@ impl From for MinCount { #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct ExcludeWords(pub Vec); +impl Display for ExcludeWords { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.join(",")) + } +} + impl From> for ExcludeWords { fn from(raw: Vec) -> Self { Self(raw) diff --git a/src/lib.rs b/src/lib.rs index df61676..49dfebe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,12 @@ pub struct WordTally { /// Ordered pairs of words and the count of times they appear. tally: Box<[(Box, usize)]>, + /// Word tallying options like case normalization and sort order. + options: Options, + + /// Filters that limit words from being tallied. + filters: Filters, + /// The sum of all words tallied. count: usize, @@ -74,6 +80,8 @@ impl WordTally { let uniq_count = tally.len(); let mut word_tally = Self { tally, + options, + filters, count, uniq_count, }; @@ -97,6 +105,16 @@ impl WordTally { self.tally } + /// Gets the `options` field. + pub const fn options(&self) -> Options { + self.options + } + + /// Gets the `filters` field. + pub fn filters(&self) -> Filters { + self.filters.clone() + } + /// Gets the `uniq_count` field. pub const fn uniq_count(&self) -> usize { self.uniq_count diff --git a/src/main.rs b/src/main.rs index 601758d..f47e5ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,12 +38,7 @@ fn main() -> Result<()> { let word_tally = WordTally::new(reader, options, filters); if args.verbose { - let verbose = Verbose { - case: args.case, - sort: args.sort, - min_chars: args.min_chars, - min_count: args.min_count, - }; + let verbose = Verbose {}; let mut stderr_output = Output::stderr(); verbose.log( diff --git a/src/options.rs b/src/options.rs index bb3de81..bd6d0ee 100644 --- a/src/options.rs +++ b/src/options.rs @@ -4,14 +4,14 @@ use core::cmp::Reverse; use core::fmt::{self, Display, Formatter}; // Tallying options. -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] pub struct Options { pub case: Case, pub sort: Sort, } /// Word case normalization. -#[derive(Clone, Copy, Debug, Default, ValueEnum)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, ValueEnum)] pub enum Case { Original, Upper, @@ -43,7 +43,7 @@ impl Display for Case { } /// Sort order by count. -#[derive(Clone, Copy, Debug, Default, ValueEnum)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, ValueEnum)] pub enum Sort { #[default] Desc, diff --git a/src/verbose.rs b/src/verbose.rs index df7d8e7..4206562 100644 --- a/src/verbose.rs +++ b/src/verbose.rs @@ -1,14 +1,8 @@ use crate::output::Output; use anyhow::Result; -use word_tally::{Case, Sort, WordTally}; +use word_tally::WordTally; -/// `Verbose` contains some config details to be logged. -pub struct Verbose { - pub case: Case, - pub sort: Sort, - pub min_chars: Option, - pub min_count: Option, -} +pub struct Verbose; impl Verbose { /// Log word tally details to stderr. @@ -23,10 +17,28 @@ impl Verbose { self.write_entry(stderr, "total-words", delimiter, word_tally.count())?; self.write_entry(stderr, "unique-words", delimiter, word_tally.uniq_count())?; self.write_entry(stderr, "delimiter", delimiter, format!("{:?}", delimiter))?; - self.write_entry(stderr, "case", delimiter, self.case)?; - self.write_entry(stderr, "order", delimiter, self.sort)?; - self.write_entry(stderr, "min-chars", delimiter, self.format(self.min_chars))?; - self.write_entry(stderr, "min-count", delimiter, self.format(self.min_count))?; + self.write_entry(stderr, "case", delimiter, word_tally.options().case)?; + self.write_entry(stderr, "order", delimiter, word_tally.options().sort)?; + + let filters = word_tally.filters(); + self.write_entry( + stderr, + "min-chars", + delimiter, + self.format(filters.min_chars), + )?; + self.write_entry( + stderr, + "min-count", + delimiter, + self.format(filters.min_count), + )?; + self.write_entry( + stderr, + "exclude-words", + delimiter, + self.format(filters.exclude), + )?; if word_tally.count() > 0 { stderr.write_line("\n")?; @@ -36,7 +48,7 @@ impl Verbose { } /// Format `"none"` or a `usize` as a `String`. - fn format(&self, value: Option) -> String { + fn format(&self, value: Option) -> String { value.map_or_else(|| "none".to_string(), |v| v.to_string()) } diff --git a/tests/main.rs b/tests/main.rs index 90873ed..079df01 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -24,7 +24,7 @@ fn verbose_without_input() { let assert = word_tally().arg("-v").assert(); assert .success() - .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count none\n") + .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count none\nexclude-words none\n") .stdout(""); } @@ -33,7 +33,7 @@ fn verbose_with_min_chars() { let assert = word_tally().arg("-v").arg("--min-chars=42").assert(); assert .success() - .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars 42\nmin-count none\n") + .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars 42\nmin-count none\nexclude-words none\n") .stdout(""); } @@ -42,7 +42,19 @@ fn verbose_with_min_count() { let assert = word_tally().arg("-v").arg("--min-count=42").assert(); assert .success() - .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count 42\n") + .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count 42\nexclude-words none\n") + .stdout(""); +} + +#[test] +fn verbose_with_exclude() { + let assert = word_tally() + .arg("-v") + .arg("--exclude=wombat,trees") + .assert(); + assert + .success() + .stderr("source -\ntotal-words 0\nunique-words 0\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count none\nexclude-words wombat,trees\n") .stdout(""); } @@ -51,7 +63,7 @@ fn verbose_with_input() { let assert = word_tally().write_stdin("wombat").arg("-v").assert(); assert .success() - .stderr("source -\ntotal-words 1\nunique-words 1\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count none\n\n") + .stderr("source -\ntotal-words 1\nunique-words 1\ndelimiter \" \"\ncase lower\norder desc\nmin-chars none\nmin-count none\nexclude-words none\n\n") .stdout("wombat 1\n"); }