ruggle_engine/
compare.rs

1use std::{
2    cmp::{max, min},
3    collections::{HashMap, HashSet},
4};
5
6use levenshtein::levenshtein;
7
8use tracing::{instrument, trace};
9
10use crate::{
11    query::*,
12    types::{self, Generics, Item},
13    Crate,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
17pub enum Similarity {
18    /// Represents how digitally similar two objects are.
19    Discrete(DiscreteSimilarity),
20
21    /// Represents how analogly similar two objects are.
22    Continuous(f32),
23}
24
25impl Similarity {
26    pub fn score(&self) -> f32 {
27        match self {
28            Discrete(Equivalent) => 0.0,
29            Discrete(Subequal) => 0.25,
30            Discrete(Different) => 1.0,
31            Continuous(s) => *s,
32        }
33    }
34}
35
36use Similarity::*;
37
38#[derive(Debug, Clone, PartialEq, Default)]
39pub struct Similarities(pub Vec<Similarity>);
40
41impl Similarities {
42    /// Calculate objective similarity for sorting.
43    pub fn score(&self) -> f32 {
44        let sum: f32 = self.0.iter().map(|sim| sim.score()).sum();
45        sum / self.0.len() as f32
46    }
47}
48
49impl PartialOrd for Similarities {
50    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
51        (self.score()).partial_cmp(&other.score())
52    }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
56pub enum DiscreteSimilarity {
57    /// Indicates that two types are the same.
58    ///
59    /// For example:
60    /// - `i32` and `i32`
61    /// - `Result<i32, ()>` and `Result<i32, ()>`
62    Equivalent,
63
64    /// Indicates that two types are partially equal.
65    ///
66    /// For example:
67    /// - an unbound generic type `T` and `i32`
68    /// - an unbound generic type `T` and `Option<U>`
69    Subequal,
70
71    /// Indicates that two types are not similar at all.
72    ///
73    /// For example:
74    /// - `i32` and `Option<bool>`
75    Different,
76}
77
78use DiscreteSimilarity::*;
79
80pub trait Compare<Rhs> {
81    fn compare(
82        &self,
83        rhs: &Rhs,
84        krate: &Crate,
85        generics: &mut Generics,
86        substs: &mut HashMap<String, Type>,
87    ) -> Vec<Similarity>;
88}
89
90impl Compare<Item> for Query {
91    #[instrument(skip(krate))]
92    fn compare(
93        &self,
94        item: &Item,
95        krate: &Crate,
96        generics: &mut Generics,
97        substs: &mut HashMap<String, Type>,
98    ) -> Vec<Similarity> {
99        let mut sims = vec![];
100
101        match (&self.name, &item.name) {
102            (Some(q), Some(i)) => sims.append(&mut q.compare(i, krate, generics, substs)),
103            (Some(_), None) => sims.push(Discrete(Different)),
104            _ => {}
105        }
106        trace!(?sims);
107
108        if let Some(ref kind) = self.kind {
109            sims.append(&mut kind.compare(&item.inner, krate, generics, substs))
110        }
111        trace!(?sims);
112
113        sims
114    }
115}
116
117impl Compare<String> for Symbol {
118    #[instrument]
119    fn compare(
120        &self,
121        symbol: &String,
122        _: &Crate,
123        _: &mut Generics,
124        _: &mut HashMap<String, Type>,
125    ) -> Vec<Similarity> {
126        use std::cmp::max;
127
128        let symbol = symbol.split("::").last().unwrap(); // SAFETY: `symbol` is not empty.
129        vec![Continuous(
130            levenshtein(self, symbol) as f32 / max(self.len(), symbol.len()) as f32,
131        )]
132    }
133}
134
135impl Compare<types::ItemEnum> for QueryKind {
136    #[instrument(skip(krate))]
137    fn compare(
138        &self,
139        kind: &types::ItemEnum,
140        krate: &Crate,
141        generics: &mut Generics,
142        substs: &mut HashMap<String, Type>,
143    ) -> Vec<Similarity> {
144        use types::ItemEnum::*;
145        use QueryKind::*;
146
147        match (self, kind) {
148            (FunctionQuery(q), Function(i)) => q.compare(i, krate, generics, substs),
149            // (FunctionQuery(q), Method(i)) => q.compare(i, krate, generics, substs),
150            (FunctionQuery(_), _) => vec![Discrete(Different)],
151        }
152    }
153}
154
155impl Compare<Qualifier> for Qualifier {
156    #[instrument]
157    fn compare(
158        &self,
159        qualifer: &Qualifier,
160        _: &Crate,
161        _: &mut Generics,
162        _: &mut HashMap<String, Type>,
163    ) -> Vec<Similarity> {
164        let mut sims = vec![];
165
166        if self == qualifer {
167            sims.push(Discrete(Equivalent));
168        } else {
169            sims.push(Discrete(Different));
170        }
171
172        sims
173    }
174}
175impl Compare<types::Function> for Function {
176    #[instrument(skip(krate))]
177    fn compare(
178        &self,
179        function: &types::Function,
180        krate: &Crate,
181        generics: &mut Generics,
182        substs: &mut HashMap<String, Type>,
183    ) -> Vec<Similarity> {
184        generics
185            .params
186            .append(&mut function.generics.params.clone());
187        generics
188            .where_predicates
189            .append(&mut function.generics.where_predicates.clone());
190
191        let mut sims = Vec::new();
192
193        let missing_qualifiers = self
194            .qualifiers
195            .difference(&function.header.qualifiers())
196            .cloned()
197            .collect::<HashSet<_>>();
198        let extra_qualifiers = function
199            .header
200            .qualifiers()
201            .difference(&self.qualifiers)
202            .cloned()
203            .collect::<HashSet<_>>();
204
205        for _ in missing_qualifiers {
206            sims.push(Discrete(Different));
207        }
208        for _ in extra_qualifiers {
209            sims.push(Discrete(Different));
210        }
211
212        sims.extend(self.decl.compare(&function.sig, krate, generics, substs));
213        sims
214    }
215}
216
217impl Compare<types::FunctionSignature> for FnDecl {
218    #[instrument(skip(krate))]
219    fn compare(
220        &self,
221        decl: &types::FunctionSignature,
222        krate: &Crate,
223        generics: &mut Generics,
224        substs: &mut HashMap<String, Type>,
225    ) -> Vec<Similarity> {
226        let mut sims = vec![];
227
228        if let Some(ref inputs) = self.inputs {
229            inputs.iter().enumerate().for_each(|(idx, q)| {
230                if let Some(i) = decl.inputs.get(idx) {
231                    sims.append(&mut q.compare(i, krate, generics, substs))
232                }
233            });
234
235            if inputs.len() != decl.inputs.len() {
236                let abs_diff = usize::abs_diff(inputs.len(), decl.inputs.len());
237                sims.append(&mut vec![Discrete(Different); abs_diff])
238            } else if inputs.is_empty() && decl.inputs.is_empty() {
239                sims.push(Discrete(Equivalent));
240            }
241        }
242        trace!(?sims);
243
244        if let Some(ref output) = self.output {
245            sims.append(&mut output.compare(&decl.output, krate, generics, substs));
246        }
247        trace!(?sims);
248
249        sims
250    }
251}
252
253impl Compare<(String, types::Type)> for Argument {
254    #[instrument(skip(krate))]
255    fn compare(
256        &self,
257        arg: &(String, types::Type),
258        krate: &Crate,
259        generics: &mut Generics,
260        substs: &mut HashMap<String, Type>,
261    ) -> Vec<Similarity> {
262        let mut sims = vec![];
263
264        if let Some(ref name) = self.name {
265            sims.append(&mut name.compare(&arg.0, krate, generics, substs));
266        }
267        trace!(?sims);
268
269        if let Some(ref type_) = self.ty {
270            sims.append(&mut type_.compare(&arg.1, krate, generics, substs));
271        }
272        trace!(?sims);
273
274        sims
275    }
276}
277
278impl Compare<Option<types::Type>> for FnRetTy {
279    #[instrument(skip(krate))]
280    fn compare(
281        &self,
282        ret_ty: &Option<types::Type>,
283        krate: &Crate,
284        generics: &mut Generics,
285        substs: &mut HashMap<String, Type>,
286    ) -> Vec<Similarity> {
287        match (self, ret_ty) {
288            (FnRetTy::Return(q), Some(i)) => q.compare(i, krate, generics, substs),
289            (FnRetTy::DefaultReturn, None) => vec![Discrete(Equivalent)],
290            _ => vec![Discrete(Different)],
291        }
292    }
293}
294
295fn compare_type(
296    lhs: &Type,
297    rhs: &types::Type,
298    krate: &Crate,
299    generics: &mut Generics,
300    substs: &mut HashMap<String, Type>,
301    _allow_recursion: bool,
302) -> Vec<Similarity> {
303    use {crate::query::Type::*, types::Type};
304
305    match (lhs, rhs) {
306        (q, Type::Generic(i)) if i == "Self" => {
307            let mut i = None;
308            for where_predicate in &generics.where_predicates {
309                if let types::WherePredicate::EqPredicate {
310                    lhs: Type::Generic(lhs),
311                    rhs,
312                } = where_predicate
313                {
314                    if lhs == "Self" {
315                        i = Some(rhs).cloned();
316                        break;
317                    }
318                }
319            }
320
321            match i {
322                None => {
323                    // FIXME: Previously this code assumed `i` is always found, but it apparently changed.
324                    vec![Discrete(Subequal)]
325                }
326                Some(i) => q.compare(&i, krate, generics, substs),
327            }
328        }
329        (q, Type::Generic(i)) => match substs.get(i) {
330            Some(i) => {
331                if q == i {
332                    vec![Discrete(Equivalent)]
333                } else {
334                    vec![Discrete(Different)]
335                }
336            }
337            None => {
338                substs.insert(i.clone(), q.clone());
339                vec![Discrete(Subequal)]
340            }
341        },
342        // FIXME: Check what happened to typedefs
343        // (q, Type::ResolvedPath { id, .. })
344        //     if krate
345        //         .index
346        //         .get(id)
347        //         .map(|i| matches!(i.inner, types::ItemEnum::Typedef(_)))
348        //         .unwrap_or(false)
349        //         && allow_recursion =>
350        // {
351        //     let sims_typedef = compare_type(lhs, rhs, krate, generics, substs, false);
352        //     // if let Some(Item {
353        //     //     inner: types::ItemEnum::Typedef(types::Typedef { type_: ref i, .. }),
354        //     //     ..
355        //     // }) = krate.index.get(id)
356        //     // {
357        //     //     // TODO: Acknowledge `generics` of `types::Typedef` to get more accurate search results.
358        //     //     let sims_adt = q.compare(i, krate, generics, substs);
359        //     //     let sum =
360        //     //         |sims: &Vec<Similarity>| -> f32 { sims.iter().map(Similarity::score).sum() };
361        //     //     if sum(&sims_adt) < sum(&sims_typedef) {
362        //     //         return sims_adt;
363        //     //     }
364        //     // }
365        //     sims_typedef
366        // }
367        (Tuple(q), Type::Tuple(i)) => {
368            let mut sims = q
369                .iter()
370                .zip(i.iter())
371                .filter_map(|(q, i)| q.as_ref().map(|q| q.compare(i, krate, generics, substs)))
372                .flatten()
373                .collect::<Vec<_>>();
374
375            // They are both tuples.
376            sims.push(Discrete(Equivalent));
377
378            // FIXME: Replace this line below with `usize::abs_diff` once it got stablized.
379            let abs_diff = max(q.len(), i.len()) - min(q.len(), i.len());
380            sims.append(&mut vec![Discrete(Different); abs_diff]);
381
382            sims
383        }
384        (Slice(q), Type::Slice(i)) => {
385            // They are both slices.
386            let mut sims = vec![Discrete(Equivalent)];
387
388            if let Some(q) = q {
389                sims.append(&mut q.compare(i.as_ref(), krate, generics, substs));
390            }
391
392            sims
393        }
394        (
395            RawPointer {
396                mutable: q_mut,
397                type_: q,
398            },
399            Type::RawPointer {
400                is_mutable: i_mut,
401                type_: i,
402            },
403        )
404        | (
405            BorrowedRef {
406                mutable: q_mut,
407                type_: q,
408            },
409            Type::BorrowedRef {
410                is_mutable: i_mut,
411                type_: i,
412                ..
413            },
414        ) => {
415            if q_mut == i_mut {
416                q.compare(i.as_ref(), krate, generics, substs)
417            } else {
418                let mut sims = q.compare(i.as_ref(), krate, generics, substs);
419                sims.push(Discrete(Subequal));
420                sims
421            }
422        }
423        (q, Type::RawPointer { type_: i, .. } | Type::BorrowedRef { type_: i, .. }) => {
424            let mut sims = q.compare(i.as_ref(), krate, generics, substs);
425            sims.push(Discrete(Subequal));
426            sims
427        }
428        (RawPointer { type_: q, .. } | BorrowedRef { type_: q, .. }, i) => {
429            let mut sims = q.compare(i, krate, generics, substs);
430            sims.push(Discrete(Subequal));
431            sims
432        }
433        (
434            UnresolvedPath {
435                name: q,
436                args: q_args,
437            },
438            Type::ResolvedPath(types::Path {
439                path: i,
440                args: i_args,
441                ..
442            }),
443        ) => {
444            let mut sims = q.compare(i, krate, generics, substs);
445
446            match (q_args, i_args) {
447                (Some(q), Some(i)) => match (&**q, &**i) {
448                    (
449                        GenericArgs::AngleBracketed { args: ref q },
450                        types::GenericArgs::AngleBracketed { args: ref i, .. },
451                    ) => {
452                        let q = q.iter().map(|q| {
453                            q.as_ref().map(|q| match q {
454                                GenericArg::Type(q) => q,
455                            })
456                        });
457                        let i = i.iter().map(|i| match i {
458                            types::GenericArg::Type(t) => Some(t),
459                            _ => None,
460                        });
461                        q.zip(i).for_each(|(q, i)| match (q, i) {
462                            (Some(q), Some(i)) => {
463                                sims.append(&mut q.compare(i, krate, generics, substs))
464                            }
465                            (Some(_), None) => sims.push(Discrete(Different)),
466                            (None, _) => {}
467                        });
468                    }
469                    // TODO: Support `GenericArgs::Parenthesized`.
470                    (_, _) => {}
471                },
472                (Some(q), None) => {
473                    let GenericArgs::AngleBracketed { args: ref q } = **q;
474                    sims.append(&mut vec![Discrete(Different); q.len()])
475                }
476                (None, _) => {}
477            }
478
479            sims
480        }
481        (Primitive(q), Type::Primitive(i)) => q.compare(i, krate, generics, substs),
482        _ => vec![Discrete(Different)],
483    }
484}
485
486impl Compare<types::Type> for Type {
487    #[instrument(skip(krate))]
488    fn compare(
489        &self,
490        type_: &types::Type,
491        krate: &Crate,
492        generics: &mut Generics,
493        substs: &mut HashMap<String, Type>,
494    ) -> Vec<Similarity> {
495        compare_type(self, type_, krate, generics, substs, true)
496    }
497}
498
499impl Compare<types::Term> for Type {
500    #[instrument(skip(krate))]
501    fn compare(
502        &self,
503        type_: &types::Term,
504        krate: &Crate,
505        generics: &mut Generics,
506        substs: &mut HashMap<String, Type>,
507    ) -> Vec<Similarity> {
508        match type_ {
509            types::Term::Type(i) => compare_type(self, i, krate, generics, substs, true),
510            _ => todo!("comparing Type with non-Type Term is not supported yet"),
511        }
512    }
513}
514
515impl Compare<String> for PrimitiveType {
516    #[instrument]
517    fn compare(
518        &self,
519        prim_ty: &String,
520        _: &Crate,
521        _: &mut Generics,
522        _: &mut HashMap<String, Type>,
523    ) -> Vec<Similarity> {
524        if self.as_str() == prim_ty {
525            vec![Discrete(Equivalent)]
526        } else {
527            vec![Discrete(Different)]
528        }
529    }
530}