From 9fe1259bdd413ed04eaf03cc7498d76ea83f52ea Mon Sep 17 00:00:00 2001
From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com>
Date: Wed, 2 Feb 2022 01:51:47 +0900
Subject: [PATCH] Support providing distance
---
Cargo.lock | 4 +-
Cargo.toml | 6 +--
README.md | 19 ++++++---
src/main.rs | 10 +++--
suggestion/Cargo.toml | 6 +--
suggestion/README.md | 34 ++++++++++++++++-
suggestion/src/lib.rs | 89 ++++++++++++++++++++++++++++++++-----------
7 files changed, 126 insertions(+), 42 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 0be5d77..08d9d20 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -169,14 +169,14 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "suggestion"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"lev_distance",
]
[[package]]
name = "suggestion-cli"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"clap",
"suggestion",
diff --git a/Cargo.toml b/Cargo.toml
index 9f2b2ef..b0a6b9c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "suggestion-cli"
-version = "0.2.0"
+version = "0.3.0"
edition = "2021"
authors = ["Ken Matsui <26405363+ken-matsui@users.noreply.github.com>"]
description = "A CLI tool for similar name suggestions to provide helps like \"Did you mean?\""
@@ -8,7 +8,7 @@ license = "MIT"
readme = "README.md"
repository = "https://github.com/ken-matsui/suggestion/"
homepage = "https://github.com/ken-matsui/suggestion#readme"
-documentation = "https://docs.rs/suggest"
+documentation = "https://docs.rs/suggestion-cli"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,7 +17,7 @@ members = ["suggestion"]
[dependencies]
clap = { version = "3.0.13", features = ["derive"] }
-suggestion = { path = "suggestion", version = "0.2.0" }
+suggestion = { path = "suggestion", version = "0.3.0" }
[[bin]]
name = "suggest"
diff --git a/README.md b/README.md
index 545f4b5..f1fca86 100644
--- a/README.md
+++ b/README.md
@@ -14,19 +14,20 @@ cargo install suggestion-cli
```bash
$ suggest --help
-suggestion-cli 0.1.0
+suggestion-cli 0.3.0
A CLI tool for similar name suggestions to provide helps like "Did you mean?"
USAGE:
- suggest [POSSIBLE_VALUES]...
+ suggest [OPTIONS] [VALUES]...
ARGS:
- Input to check if similar name exists
- ... Values of similar names
+ Input to check if similar name exists
+ ... Values of similar names
OPTIONS:
- -h, --help Print help information
- -V, --version Print version information
+ -d, --distance Levenshtein Distance
+ -h, --help Print help information
+ -V, --version Print version information
```
## Examples
@@ -40,6 +41,12 @@ No similar name for the `hoge` input was found.
$ suggest install update install
The same value with the `install` input exists.
+
+$ suggest paoc poac poacpp
+No similar name for the `paoc` input was found.
+
+$ suggest paoc poac poacpp --distance 2
+The `paoc` input is similar to `poac`.
```
## Contribution
diff --git a/src/main.rs b/src/main.rs
index c4571c5..0f4441f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,16 +8,20 @@ struct Args {
input: String,
/// Values of similar names
- possible_values: Vec,
+ values: Vec,
+
+ /// Levenshtein Distance
+ #[clap(short, long)]
+ distance: Option,
}
fn main() {
let args = Args::parse();
- let exit_code = if args.possible_values.contains(&args.input) {
+ let exit_code = if args.values.contains(&args.input) {
eprintln!("The same value with the `{}` input exists.", args.input);
1
- } else if let Some(sugg) = args.possible_values.suggest(&args.input) {
+ } else if let Some(sugg) = args.values.suggest_with_dist(&args.input, args.distance) {
println!("The `{}` input is similar to `{}`.", args.input, sugg);
0
} else {
diff --git a/suggestion/Cargo.toml b/suggestion/Cargo.toml
index 7724b3d..c92a318 100644
--- a/suggestion/Cargo.toml
+++ b/suggestion/Cargo.toml
@@ -1,13 +1,13 @@
[package]
name = "suggestion"
-version = "0.2.0"
+version = "0.3.0"
edition = "2021"
authors = ["Ken Matsui <26405363+ken-matsui@users.noreply.github.com>"]
description = "A minimal library for similar name suggestions to provide helps like \"Did you mean?\""
license = "MIT"
readme = "README.md"
-repository = "https://github.com/ken-matsui/suggestion/suggestion/"
-homepage = "https://github.com/ken-matsui/suggestion/suggestion#readme"
+repository = "https://github.com/ken-matsui/suggestion/tree/main/suggestion/"
+homepage = "https://github.com/ken-matsui/suggestion/tree/main/suggestion#readme"
documentation = "https://docs.rs/suggestion"
[dependencies]
diff --git a/suggestion/README.md b/suggestion/README.md
index c977d2f..7854ac6 100644
--- a/suggestion/README.md
+++ b/suggestion/README.md
@@ -3,7 +3,9 @@
A minimal library for similar name suggestions to provide helps like "Did you mean?"
This library provides suggestion traits for all collection types in the standard library.
-## Example
+## Examples
+
+### Simple case
This example can be executed by the `cargo run --example simple` command.
@@ -31,9 +33,35 @@ No command named `instakk` found.
Did you mean `install`?
```
+### Specifying distance
+
+```rust
+use suggestion::Suggest;
+
+fn main() {
+ let input = "paoc";
+
+ let list_commands = vec!["poac", "poacpp"];
+ if list_commands.contains(&input) {
+ return;
+ }
+
+ if let Some(sugg) = list_commands.suggest_with_dist(input, Some(2)) {
+ println!("No command named `{}` found.", input);
+ println!("Did you mean `{}`?", sugg);
+ }
+}
+```
+
+```shell
+$ cargo run
+No command named `paoc` found.
+Did you mean `poac`?
+```
+
## Supported types
-Please let me know by issues or pull requests if there is anything left out.
+Please let me know if anything is left out through issues or pull requests.
### Sequences
@@ -46,6 +74,8 @@ Please let me know by issues or pull requests if there is anything left out.
* `HashMap`
* `BTreeMap`
+To suggest keys, use `suggestion::SuggestKey` trait.
+
### Sets
* `BTreeSet`
diff --git a/suggestion/src/lib.rs b/suggestion/src/lib.rs
index 2ac0864..7e38866 100644
--- a/suggestion/src/lib.rs
+++ b/suggestion/src/lib.rs
@@ -44,10 +44,16 @@ use std::collections::BinaryHeap;
pub trait Suggest {
/// Find similar name in values for all collections
fn suggest(&self, query: &str) -> Option;
+
+ /// Find similar name with dist in values for all collections
+ fn suggest_with_dist(&self, query: &str, dist: Option) -> Option;
}
pub trait SuggestKey {
/// Find similar name in keys for Map collections
fn suggest_key(&self, query: &str) -> Option;
+
+ /// Find similar name with dist in keys for Map collections
+ fn suggest_key_with_dist(&self, query: &str, dist: Option) -> Option;
}
macro_rules! impl_suggest {
@@ -56,6 +62,9 @@ macro_rules! impl_suggest {
fn suggest(&self, query: &str) -> Option {
find_best_match_for_name(self.iter(), query, None)
}
+ fn suggest_with_dist(&self, query: &str, dist: Option) -> Option {
+ find_best_match_for_name(self.iter(), query, dist)
+ }
}
};
}
@@ -65,6 +74,9 @@ macro_rules! impl_suggest_key {
fn suggest_key(&self, query: &str) -> Option {
find_best_match_for_name(self.keys(), query, None)
}
+ fn suggest_key_with_dist(&self, query: &str, dist: Option) -> Option {
+ find_best_match_for_name(self.keys(), query, dist)
+ }
}
};
}
@@ -74,6 +86,9 @@ macro_rules! impl_suggest_value {
fn suggest(&self, query: &str) -> Option {
find_best_match_for_name(self.values(), query, None)
}
+ fn suggest_with_dist(&self, query: &str, dist: Option) -> Option {
+ find_best_match_for_name(self.values(), query, dist)
+ }
}
};
}
@@ -83,6 +98,9 @@ impl, const N: usize> Suggest for [T; N] {
fn suggest(&self, query: &str) -> Option {
find_best_match_for_name(self.iter(), query, None)
}
+ fn suggest_with_dist(&self, query: &str, dist: Option) -> Option {
+ find_best_match_for_name(self.iter(), query, dist)
+ }
}
// Slices
@@ -90,6 +108,9 @@ impl> Suggest for [T] {
fn suggest(&self, query: &str) -> Option {
find_best_match_for_name(self.iter(), query, None)
}
+ fn suggest_with_dist(&self, query: &str, dist: Option) -> Option {
+ find_best_match_for_name(self.iter(), query, dist)
+ }
}
// Sequences
@@ -114,12 +135,39 @@ impl_suggest!(BinaryHeap);
mod tests {
use super::*;
+ macro_rules! test_suggest_primitive {
+ ($t:ty, $f:ident) => {
+ #[test]
+ fn $f() {
+ let tmp = ["aaab", "aaabc"];
+ let input: $t = &tmp;
+ assert_eq!(input.suggest("aaaa"), Some("aaab".to_string()));
+
+ let tmp = ["poac", "poacpp"];
+ let input: $t = &tmp;
+ assert_eq!(input.suggest("paoc"), None);
+ assert_eq!(input.suggest_with_dist("paoc", Some(1)), None);
+ assert_eq!(
+ input.suggest_with_dist("paoc", Some(2)),
+ Some("poac".to_string())
+ );
+ }
+ };
+ }
macro_rules! test_suggest {
($t:ident, $f:ident) => {
#[test]
fn $f() {
let input: $t<_> = vec!["aaab", "aaabc"].into_iter().collect();
assert_eq!(input.suggest("aaaa"), Some("aaab".to_string()));
+
+ let input: $t<_> = vec!["poac", "poacpp"].into_iter().collect();
+ assert_eq!(input.suggest("paoc"), None);
+ assert_eq!(input.suggest_with_dist("paoc", Some(1)), None);
+ assert_eq!(
+ input.suggest_with_dist("paoc", Some(2)),
+ Some("poac".to_string())
+ );
}
};
}
@@ -134,35 +182,30 @@ mod tests {
let input = $t::<_, _>::from_iter(IntoIter::new([(2, "aaab"), (4, "aaabc")]));
assert_eq!(input.suggest("aaaa"), Some("aaab".to_string()));
+
+ let input = $t::<_, _>::from_iter(IntoIter::new([("poac", 2), ("poacpp", 4)]));
+ assert_eq!(input.suggest_key("paoc"), None);
+ assert_eq!(input.suggest_key_with_dist("paoc", Some(1)), None);
+ assert_eq!(
+ input.suggest_key_with_dist("paoc", Some(2)),
+ Some("poac".to_string())
+ );
+
+ let input = $t::<_, _>::from_iter(IntoIter::new([(2, "poac"), (4, "poacpp")]));
+ assert_eq!(input.suggest("paoc"), None);
+ assert_eq!(input.suggest_with_dist("paoc", Some(1)), None);
+ assert_eq!(
+ input.suggest_with_dist("paoc", Some(2)),
+ Some("poac".to_string())
+ );
}
};
}
// Primitive Array Type
- #[test]
- fn test_array() {
- let input = ["aaab", "aaabc"];
- assert_eq!(input.suggest("aaaa"), Some("aaab".to_string()));
-
- let ref input = ["aaab", "aaabc"];
- assert_eq!(input.suggest("aaaa"), Some("aaab".to_string()));
-
- let ref mut input = ["aaab", "aaabc"];
- assert_eq!(input.suggest("aaaa"), Some("aaab".to_string()));
- }
-
+ test_suggest_primitive!(&[&str; 2], test_array);
// Slices
- #[test]
- fn test_slices() {
- let input = ["", "aaab", "aaabc"];
- assert_eq!(input[1..].suggest("aaaa"), Some("aaab".to_string()));
-
- let ref input = ["", "aaab", "aaabc"];
- assert_eq!(input[1..].suggest("aaaa"), Some("aaab".to_string()));
-
- let ref mut input = ["", "aaab", "aaabc"];
- assert_eq!(input[1..].suggest("aaaa"), Some("aaab".to_string()));
- }
+ test_suggest_primitive!(&[&str], test_slices);
// Sequences
test_suggest!(LinkedList, test_suggest_linked_list);