analysis/
analysis.rs

1use matchete::{Matcher, Custom, Similarity};
2
3// Levenshtein metric implementation
4struct LevenshteinMetric;
5
6impl Similarity<String, String> for LevenshteinMetric {
7    fn score(&self, query: &String, candidate: &String) -> f64 {
8        let distance = levenshtein_distance(query, candidate);
9        let max_len = query.len().max(candidate.len());
10        if max_len == 0 { 1.0 } else { 1.0 - (distance as f64 / max_len as f64) }
11    }
12
13    fn exact(&self, query: &String, candidate: &String) -> bool {
14        query == candidate
15    }
16}
17
18// Jaccard metric implementation
19struct JaccardMetric;
20
21impl Similarity<String, String> for JaccardMetric {
22    fn score(&self, query: &String, candidate: &String) -> f64 {
23        let query_chars: std::collections::HashSet<char> = query.chars().collect();
24        let candidate_chars: std::collections::HashSet<char> = candidate.chars().collect();
25
26        let intersection = query_chars.intersection(&candidate_chars).count();
27        let union = query_chars.union(&candidate_chars).count();
28
29        if union == 0 { 1.0 } else { intersection as f64 / union as f64 }
30    }
31}
32
33fn levenshtein_distance(a: &str, b: &str) -> usize {
34    let len_a = a.len();
35    let len_b = b.len();
36
37    if len_a == 0 { return len_b; }
38    if len_b == 0 { return len_a; }
39
40    let mut matrix = vec![vec![0; len_b + 1]; len_a + 1];
41
42    for i in 0..=len_a { matrix[i][0] = i; }
43    for j in 0..=len_b { matrix[0][j] = j; }
44
45    for i in 1..=len_a {
46        for j in 1..=len_b {
47            let cost = if a.chars().nth(i - 1) == b.chars().nth(j - 1) { 0 } else { 1 };
48            matrix[i][j] = (matrix[i - 1][j] + 1)
49                .min(matrix[i][j - 1] + 1)
50                .min(matrix[i - 1][j - 1] + cost);
51        }
52    }
53
54    matrix[len_a][len_b]
55}
56
57fn main() {
58    // Create a matcher with two metrics
59    let matcher = Matcher::<String, String>::new()
60        .add(LevenshteinMetric, 0.7) // 70% weight
61        .add(JaccardMetric, 0.3)     // 30% weight
62        .threshold(0.5);
63
64    // Define the query and candidate
65    let query = String::from("test");
66    let candidate = String::from("tent");
67
68    // Perform detailed analysis
69    println!("Detailed Analysis Example");
70    println!("========================");
71    let analysis = matcher.analyze(&query, &candidate);
72    println!("Query: {}", analysis.query);
73    println!("Candidate: {}", analysis.candidate);
74    println!("Overall score: {:.2}", analysis.score);
75    println!("Exact match: {}", analysis.exact);
76    println!("Is match: {}", matcher.matches(&query, &candidate));
77    println!("Individual metric scores:");
78    for (i, score) in analysis.scores.iter().enumerate() {
79        println!(
80            "  Metric {}:\n    Raw score: {:.2}\n    Weight: {:.2}\n    Weighted score: {:.2}",
81            i + 1, score.value, score.weight, score.weighted()
82        );
83    }
84}