DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (6863f516ba38)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
use app::App;
// Third Party
#[cfg(feature = "suggestions")]
use strsim;

// Internal
use fmt::Format;

/// Produces a string from a given list of possible values which is similar to
/// the passed in value `v` with a certain confidence.
/// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield
/// `Some("foo")`, whereas "blark" would yield `None`.
#[cfg(feature = "suggestions")]
#[cfg_attr(feature = "lints", allow(needless_lifetimes))]
pub fn did_you_mean<'a, T: ?Sized, I>(v: &str, possible_values: I) -> Option<&'a str>
where
    T: AsRef<str> + 'a,
    I: IntoIterator<Item = &'a T>,
{
    let mut candidate: Option<(f64, &str)> = None;
    for pv in possible_values {
        let confidence = strsim::jaro_winkler(v, pv.as_ref());
        if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence))
        {
            candidate = Some((confidence, pv.as_ref()));
        }
    }
    match candidate {
        None => None,
        Some((_, candidate)) => Some(candidate),
    }
}

#[cfg(not(feature = "suggestions"))]
pub fn did_you_mean<'a, T: ?Sized, I>(_: &str, _: I) -> Option<&'a str>
where
    T: AsRef<str> + 'a,
    I: IntoIterator<Item = &'a T>,
{
    None
}

/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
#[cfg_attr(feature = "lints", allow(needless_lifetimes))]
pub fn did_you_mean_flag_suffix<'z, T, I>(
    arg: &str,
    longs: I,
    subcommands: &'z [App],
) -> (String, Option<&'z str>)
where
    T: AsRef<str> + 'z,
    I: IntoIterator<Item = &'z T>,
{
    match did_you_mean(arg, longs) {
        Some(candidate) => {
            let suffix = format!(
                "\n\tDid you mean {}{}?",
                Format::Good("--"),
                Format::Good(candidate)
            );
            return (suffix, Some(candidate));
        }
        None => for subcommand in subcommands {
            let opts = subcommand
                .p
                .flags
                .iter()
                .filter_map(|f| f.s.long)
                .chain(subcommand.p.opts.iter().filter_map(|o| o.s.long));

            if let Some(candidate) = did_you_mean(arg, opts) {
                let suffix = format!(
                    "\n\tDid you mean to put '{}{}' after the subcommand '{}'?",
                    Format::Good("--"),
                    Format::Good(candidate),
                    Format::Good(subcommand.get_name())
                );
                return (suffix, Some(candidate));
            }
        },
    }
    (String::new(), None)
}

/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
pub fn did_you_mean_value_suffix<'z, T, I>(arg: &str, values: I) -> (String, Option<&'z str>)
where
    T: AsRef<str> + 'z,
    I: IntoIterator<Item = &'z T>,
{
    match did_you_mean(arg, values) {
        Some(candidate) => {
            let suffix = format!("\n\tDid you mean '{}'?", Format::Good(candidate));
            (suffix, Some(candidate))
        }
        None => (String::new(), None),
    }
}

#[cfg(all(test, features = "suggestions"))]
mod test {
    use super::*;

    #[test]
    fn possible_values_match() {
        let p_vals = ["test", "possible", "values"];
        assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test"));
    }

    #[test]
    fn possible_values_nomatch() {
        let p_vals = ["test", "possible", "values"];
        assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none());
    }

    #[test]
    fn suffix_long() {
        let p_vals = ["test", "possible", "values"];
        let suffix = "\n\tDid you mean \'--test\'?";
        assert_eq!(
            did_you_mean_flag_suffix("tst", p_vals.iter(), []),
            (suffix, Some("test"))
        );
    }

    #[test]
    fn suffix_enum() {
        let p_vals = ["test", "possible", "values"];
        let suffix = "\n\tDid you mean \'test\'?";
        assert_eq!(
            did_you_mean_value_suffix("tst", p_vals.iter()),
            (suffix, Some("test"))
        );
    }
}