Skip to content

Commit

Permalink
Suggest valid message or constructor name, when misspelled (#1162)
Browse files Browse the repository at this point in the history
* Suggest valid message or constructor name, when misspelled

* add question mark

* remove pub
  • Loading branch information
pgherveou authored Jun 15, 2023
1 parent 71a8a42 commit 0376b34
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/transcode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ scale-info = { version = "2.7.0", default-features = false, features = ["derive"
serde = { version = "1.0.164", default-features = false, features = ["derive"] }
serde_json = "1.0.96"
thiserror = "1.0.40"
strsim = "0.10.0"

[dev-dependencies]
assert_matches = "1.5.0"
Expand Down
43 changes: 41 additions & 2 deletions crates/transcode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ use ink_metadata::{
InkProject,
MessageSpec,
};
use itertools::Itertools;
use scale::{
Compact,
Decode,
Expand All @@ -142,6 +143,7 @@ use scale_info::{
Field,
};
use std::{
cmp::Ordering,
fmt::Debug,
path::Path,
};
Expand All @@ -153,6 +155,24 @@ pub struct ContractMessageTranscoder {
transcoder: Transcoder,
}

/// Find strings from an iterable of `possible_values` similar to a given value `v`
/// Returns a Vec of all possible values that exceed a similarity threshold
/// sorted by ascending similarity, most similar comes last
/// Extracted from https://github.com/clap-rs/clap/blob/v4.3.4/clap_builder/src/parser/features/suggestions.rs#L11-L26
fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
where
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
let mut candidates: Vec<(f64, String)> = possible_values
.into_iter()
.map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
.filter(|(confidence, _)| *confidence > 0.7)
.collect();
candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
candidates.into_iter().map(|(_, pv)| pv).collect()
}

impl ContractMessageTranscoder {
pub fn new(metadata: InkProject) -> Self {
let transcoder = TranscoderBuilder::new(metadata.registry())
Expand Down Expand Up @@ -203,9 +223,18 @@ impl ContractMessageTranscoder {
))
}
(None, None) => {
let constructors = self.constructors().map(|c| c.label());
let messages = self.messages().map(|c| c.label());
let possible_values: Vec<_> = constructors.chain(messages).collect();
let help_txt = did_you_mean(name, possible_values.clone())
.first()
.map(|suggestion| format!("Did you mean '{}'?", suggestion))
.unwrap_or_else(|| {
format!("Should be one of: {}", possible_values.iter().join(", "))
});

return Err(anyhow::anyhow!(
"No constructor or message with the name '{}' found",
name
"No constructor or message with the name '{name}' found.\n{help_txt}",
))
}
};
Expand Down Expand Up @@ -558,6 +587,16 @@ mod tests {
Ok(())
}

#[test]
fn encode_misspelled_arg() {
let metadata = generate_metadata();
let transcoder = ContractMessageTranscoder::new(metadata);
assert_eq!(
transcoder.encode("fip", ["true"]).unwrap_err().to_string(),
"No constructor or message with the name 'fip' found.\nDid you mean 'flip'?"
);
}

#[test]
fn encode_mismatching_args_length() {
let metadata = generate_metadata();
Expand Down

0 comments on commit 0376b34

Please sign in to comment.