roogle_engine/
compare.rs

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