multimatcher/
multimatcher.rs

1use matchete::{Matcher, MultiMatcher, Custom, Similarity};
2
3// Reuse the metrics from analysis.rs
4struct LevenshteinMetric;
5struct JaccardMetric;
6
7impl Similarity<String, String> for LevenshteinMetric {
8    fn score(&self, query: &String, candidate: &String) -> f64 {
9        let distance = levenshtein_distance(query, candidate);
10        let max_len = query.len().max(candidate.len());
11        if max_len == 0 { 1.0 } else { 1.0 - (distance as f64 / max_len as f64) }
12    }
13
14    fn exact(&self, query: &String, candidate: &String) -> bool {
15        query == candidate
16    }
17}
18
19impl Similarity<String, String> for JaccardMetric {
20    fn score(&self, query: &String, candidate: &String) -> f64 {
21        let query_chars: std::collections::HashSet<char> = query.chars().collect();
22        let candidate_chars: std::collections::HashSet<char> = candidate.chars().collect();
23
24        let intersection = query_chars.intersection(&candidate_chars).count();
25        let union = query_chars.union(&candidate_chars).count();
26
27        if union == 0 { 1.0 } else { intersection as f64 / union as f64 }
28    }
29}
30
31fn levenshtein_distance(a: &str, b: &str) -> usize {
32    // Same implementation as in analysis.rs
33    let len_a = a.len();
34    let len_b = b.len();
35
36    if len_a == 0 { return len_b; }
37    if len_b == 0 { return len_a; }
38
39    let mut matrix = vec![vec![0; len_b + 1]; len_a + 1];
40
41    for i in 0..=len_a { matrix[i][0] = i; }
42    for j in 0..=len_b { matrix[0][j] = j; }
43
44    for i in 1..=len_a {
45        for j in 1..=len_b {
46            let cost = if a.chars().nth(i - 1) == b.chars().nth(j - 1) { 0 } else { 1 };
47            matrix[i][j] = (matrix[i - 1][j] + 1)
48                .min(matrix[i][j - 1] + 1)
49                .min(matrix[i - 1][j - 1] + cost);
50        }
51    }
52
53    matrix[len_a][len_b]
54}
55
56fn main() {
57    // Create two matchers with different metrics and thresholds
58    let matcher1 = Matcher::<String, String>::new()
59        .add(LevenshteinMetric, 1.0)
60        .threshold(0.6);
61
62    let matcher2 = Matcher::<String, String>::new()
63        .add(JaccardMetric, 1.0)
64        .threshold(0.4);
65
66    // Combine them into a MultiMatcher
67    let multi_matcher = MultiMatcher::<String, String>::new()
68        .add(matcher1)
69        .add(matcher2)
70        .threshold(0.5);
71
72    // Define the query and candidate strings
73    let query = String::from("example");
74    let candidates = vec![
75        String::from("example"),
76        String::from("examp"),
77        String::from("test"),
78    ];
79
80    // Find matches with a limit of 2
81    println!("MultiMatcher Example");
82    println!("===================");
83    let matches = multi_matcher.find_limit(&query, &candidates, 2);
84    println!("Matches found: {}", matches.len());
85    for (i, m) in matches.iter().enumerate() {
86        println!(
87            "Match {}:\n  Score: {:.2}\n  Candidate: {}\n  Exact: {}",
88            i + 1, m.score, m.candidate, m.exact
89        );
90    }
91}