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
use super::index::GraphIndex;
// ============================================================================
// resolve_symbol.
// ============================================================================
#[derive(Debug, Clone, PartialEq, Eq)]
/// Represents `Resolution`.
pub enum Resolution {
/// Represents `Resolved`.
Resolved(String),
/// Represents `Ambiguous`.
Ambiguous(Vec<String>),
/// Represents `NotFound`.
NotFound,
}
/// "Never guess" resolution ladder: exact id, then unique exact name, then
/// unique case-insensitive name; two-or-more candidates at any step is reported
/// as ambiguous, not resolved further.
pub fn resolve_symbol(index: &GraphIndex, query: &str) -> Resolution {
if index.by_id.contains_key(query) {
return Resolution::Resolved(query.to_string());
}
if let Some(exact) = index.by_simple_name.get(query) {
if exact.len() == 1 {
return Resolution::Resolved(index.graph.defs[exact[0]].id.clone());
}
if exact.len() > 1 {
return Resolution::Ambiguous(
exact
.iter()
.map(|&i| index.graph.defs[i].id.clone())
.collect(),
);
}
}
let lower = query.to_lowercase();
if let Some(ci) = index.by_lower_name.get(&lower) {
if ci.len() == 1 {
return Resolution::Resolved(index.graph.defs[ci[0]].id.clone());
}
if ci.len() > 1 {
return Resolution::Ambiguous(
ci.iter().map(|&i| index.graph.defs[i].id.clone()).collect(),
);
}
}
// A TAIL of a def id, which is how a caller spells an enum member:
// `Toggles.EnableX`, never the namespace-qualified
// `App.Config.Toggles.EnableX` the graph keys it under. Reached only after
// every step above has missed, so no query that resolved before still
// resolves differently. Restricted to a dotted query on purpose -- every
// def is indexed under its simple name, so `by_simple_name` above is
// already exhaustive for an undotted one and this step could only repeat
// it. Two or more ids ending in the same tail is an ambiguity, reported as
// one rather than guessed at. Iterates `graph.defs` directly (its array
// order) rather than the unordered `by_id` map.
if query.contains('.') {
let suffix = format!(".{query}");
let tail: Vec<String> = index
.graph
.defs
.iter()
.filter(|d| d.id.ends_with(&suffix))
.map(|d| d.id.clone())
.collect();
if tail.len() == 1 {
return Resolution::Resolved(tail.into_iter().next().expect("one tail match"));
}
if tail.len() > 1 {
return Resolution::Ambiguous(tail);
}
}
Resolution::NotFound
}