diff --git a/src/names.rs b/src/names.rs index ba19aa52..76ac0ad2 100644 --- a/src/names.rs +++ b/src/names.rs @@ -1,7 +1,11 @@ /// An iterator over all possible permutations of hyphens (`-`) and underscores (`_`) of a crate name. /// -/// For instance, the name `parking_lot` is turned into the sequence `parking_lot` and `parking_lot`, while +/// The sequence yields the input name first, then an all-hyphens variant of it followed by an +/// all-underscores variant to maximize the chance of finding a match. Then follow all remaining permutations. +/// +/// For instance, the name `parking_lot` is turned into the sequence `parking_lot` and `parking-lot`, while /// `serde-yaml` is turned into `serde-yaml` and `serde_yaml`. +/// Finally, `a-b_c` is returned as `a-b_c`, `a-b-c`, `a_b_c`, `a_b-c`. #[derive(Clone)] pub struct Names { count: Option, @@ -60,8 +64,10 @@ impl Iterator for Names { return None; } + //map the count so the first value is the last one (all "-"), the second one is the first one (all "_")... + let used_count = *count as isize - 1 + self.max_count as isize; for (sep_index, char_index) in self.separator_indexes[..self.separator_count].iter().enumerate() { - let char = if *count & (1 << sep_index) == 0 { b'-' } else { b'_' }; + let char = if used_count & (1 << sep_index) == 0 { b'_' } else { b'-' }; // SAFETY: We validated that `char_index` is a valid UTF-8 codepoint #[allow(unsafe_code)] unsafe { @@ -78,4 +84,11 @@ impl Iterator for Names { } } } + + fn count(self) -> usize + where + Self: Sized, + { + self.max_count as usize + } } diff --git a/tests/names/mod.rs b/tests/names/mod.rs index 5ba90be4..6187d340 100644 --- a/tests/names/mod.rs +++ b/tests/names/mod.rs @@ -1,34 +1,32 @@ use crates_index::Names; #[test] -fn empty_string() { - assert_eq!(Names::new("").unwrap().count(), 1); +fn empty_string_is_nothing_special() { + assert_eq!(assert_count(Names::new("").unwrap()), 1); } #[test] fn name_without_separators_yields_name() { - assert_eq!(Names::new("serde").unwrap().count(), 1); + assert_eq!(assert_count(Names::new("serde").unwrap()), 1); } #[test] -fn permutation_count() { - assert_eq!(Names::new("a-b").unwrap().count(), 2); - assert_eq!(Names::new("a-b_c").unwrap().count(), 4); - assert_eq!(Names::new("a_b_c").unwrap().count(), 4); - assert_eq!(Names::new("a_b_c-d").unwrap().count(), 8); +fn permutation_counts() { + assert_eq!(assert_count(Names::new("a-b").unwrap()), 2); + assert_eq!(assert_count(Names::new("a-b_c").unwrap()), 4); + assert_eq!(assert_count(Names::new("a_b_c").unwrap()), 4); + assert_eq!(assert_count(Names::new("a_b_c-d").unwrap()), 8); } #[test] fn max_permutation_count_causes_error() { assert_eq!( - Names::new("a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p") - .expect("15 separators are fine") - .count(), + assert_count(Names::new("a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p").expect("15 separators are fine")), 32768 ); assert!( Names::new("a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r").is_none(), - "17 are not fine anymore" + "16 are not fine anymore" ); } @@ -36,19 +34,25 @@ fn max_permutation_count_causes_error() { fn permutations() { for (name, expected) in [ ("parking_lot", &["parking_lot", "parking-lot"] as &[_]), // the input name is always the first one returned. + ( + "a-b_c-d", // input name -> all-hyphens -> all_underscores -> rest + &[ + "a-b_c-d", "a-b-c-d", "a_b_c_d", "a-b_c_d", "a_b-c_d", "a-b-c_d", "a_b_c-d", "a_b-c-d", + ], + ), ("a_b", &["a_b", "a-b"]), ("a-b", &["a-b", "a_b"]), - ("a-b-c", &["a-b-c", "a_b-c", "a-b_c", "a_b_c"]), + ("a-b-c", &["a-b-c", "a_b_c", "a-b_c", "a_b-c"]), ( "a-b-c-d", &[ - "a-b-c-d", "a_b-c-d", "a-b_c-d", "a_b_c-d", "a-b-c_d", "a_b-c_d", "a-b_c_d", "a_b_c_d", + "a-b-c-d", "a_b_c_d", "a-b_c_d", "a_b-c_d", "a-b-c_d", "a_b_c-d", "a-b_c-d", "a_b-c-d", ], ), ( "a_b_c_d", &[ - "a_b_c_d", "a-b-c-d", "a_b-c-d", "a-b_c-d", "a_b_c-d", "a-b-c_d", "a_b-c_d", "a-b_c_d", + "a_b_c_d", "a-b-c-d", "a-b_c_d", "a_b-c_d", "a-b-c_d", "a_b_c-d", "a-b_c-d", "a_b-c-d", ], ), ] { @@ -56,3 +60,13 @@ fn permutations() { assert_eq!(&names, expected); } } + +fn assert_count(names: Names) -> usize { + let expected = names.clone().collect::>().len(); + assert_eq!( + names.count(), + expected, + "the computed count should match the actual one" + ); + expected +}