Skip to content

Commit

Permalink
Support providing distance
Browse files Browse the repository at this point in the history
  • Loading branch information
ken-matsui committed Feb 1, 2022
1 parent 0fea5ea commit 9fe1259
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 42 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "suggestion-cli"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["Ken Matsui <[email protected]>"]
description = "A CLI tool for similar name suggestions to provide helps like \"Did you mean?\""
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

Expand All @@ -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"
Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <INPUT> [POSSIBLE_VALUES]...
suggest [OPTIONS] <INPUT> [VALUES]...

ARGS:
<INPUT> Input to check if similar name exists
<POSSIBLE_VALUES>... Values of similar names
<INPUT> Input to check if similar name exists
<VALUES>... Values of similar names

OPTIONS:
-h, --help Print help information
-V, --version Print version information
-d, --distance <DISTANCE> Levenshtein Distance
-h, --help Print help information
-V, --version Print version information
```
## Examples
Expand All @@ -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
Expand Down
10 changes: 7 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ struct Args {
input: String,

/// Values of similar names
possible_values: Vec<String>,
values: Vec<String>,

/// Levenshtein Distance
#[clap(short, long)]
distance: Option<usize>,
}

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 {
Expand Down
6 changes: 3 additions & 3 deletions suggestion/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[package]
name = "suggestion"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["Ken Matsui <[email protected]>"]
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]
Expand Down
34 changes: 32 additions & 2 deletions suggestion/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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

Expand All @@ -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`
Expand Down
89 changes: 66 additions & 23 deletions suggestion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>;

/// Find similar name with dist in values for all collections
fn suggest_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String>;
}
pub trait SuggestKey {
/// Find similar name in keys for Map collections
fn suggest_key(&self, query: &str) -> Option<String>;

/// Find similar name with dist in keys for Map collections
fn suggest_key_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String>;
}

macro_rules! impl_suggest {
Expand All @@ -56,6 +62,9 @@ macro_rules! impl_suggest {
fn suggest(&self, query: &str) -> Option<String> {
find_best_match_for_name(self.iter(), query, None)
}
fn suggest_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String> {
find_best_match_for_name(self.iter(), query, dist)
}
}
};
}
Expand All @@ -65,6 +74,9 @@ macro_rules! impl_suggest_key {
fn suggest_key(&self, query: &str) -> Option<String> {
find_best_match_for_name(self.keys(), query, None)
}
fn suggest_key_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String> {
find_best_match_for_name(self.keys(), query, dist)
}
}
};
}
Expand All @@ -74,6 +86,9 @@ macro_rules! impl_suggest_value {
fn suggest(&self, query: &str) -> Option<String> {
find_best_match_for_name(self.values(), query, None)
}
fn suggest_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String> {
find_best_match_for_name(self.values(), query, dist)
}
}
};
}
Expand All @@ -83,13 +98,19 @@ impl<T: std::convert::AsRef<str>, const N: usize> Suggest for [T; N] {
fn suggest(&self, query: &str) -> Option<String> {
find_best_match_for_name(self.iter(), query, None)
}
fn suggest_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String> {
find_best_match_for_name(self.iter(), query, dist)
}
}

// Slices
impl<T: std::convert::AsRef<str>> Suggest for [T] {
fn suggest(&self, query: &str) -> Option<String> {
find_best_match_for_name(self.iter(), query, None)
}
fn suggest_with_dist(&self, query: &str, dist: Option<usize>) -> Option<String> {
find_best_match_for_name(self.iter(), query, dist)
}
}

// Sequences
Expand All @@ -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())
);
}
};
}
Expand All @@ -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);
Expand Down

0 comments on commit 9fe1259

Please sign in to comment.