Repository navigation
Expand file tree
/
Copy pathimport.go
More file actions
123 lines (112 loc) · 3.29 KB
/
Copy pathimport.go
File metadata and controls
123 lines (112 loc) · 3.29 KB
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package provides
import (
"context"
"fmt"
"sort"
)
// ImportRequest describes a source import and the project dependencies that
// may provide it.
type ImportRequest struct {
Language string
Name string
Packages []Package
Options SurfaceOptions
}
// ImportMatch connects an import to one matching package surface.
type ImportMatch struct {
PURL string
Provided ProvidedName
}
// ImportResult contains every package surface matching an import and any
// non-fatal diagnostics collected while resolving project surfaces.
type ImportResult struct {
Language string
Name string
Matches []ImportMatch
Diagnostics []Diagnostic
}
// ResolveImport resolves the requested project package surfaces and returns
// every package that provides the import.
func ResolveImport(
ctx context.Context,
resolver SurfaceResolver,
request ImportRequest,
) (ImportResult, error) {
if request.Language == "" {
return ImportResult{}, fmt.Errorf("resolve import: language is empty")
}
if request.Name == "" {
return ImportResult{}, fmt.Errorf("resolve import: name is empty")
}
project, err := ResolveProjectSurfaces(ctx, resolver, request.Packages, request.Options)
return MatchImport(request.Language, request.Name, project), err
}
// MatchImport returns every matching package from an already resolved project.
func MatchImport(language, name string, project ProjectSurfaceResult) ImportResult {
matches := make(map[importMatchKey]ImportMatch)
for _, surface := range project.Surfaces {
for _, provided := range surface.Provides {
if provided.Language != language || !provided.Matches(name) {
continue
}
provided.Match = normalizedMatchMode(provided.Match)
if provided.Match != MatchPrefix {
provided.Separator = ""
}
key := importMatchKey{
purl: surface.PURL,
language: provided.Language,
name: provided.Name,
kind: provided.Kind,
match: provided.Match,
separator: provided.Separator,
caseInsensitive: provided.CaseInsensitive,
}
if existing, ok := matches[key]; ok {
provided.Evidence = mergeEvidence(existing.Provided.Evidence, provided.Evidence)
} else {
provided.Evidence = mergeEvidence(provided.Evidence)
}
matches[key] = ImportMatch{PURL: surface.PURL, Provided: provided}
}
}
ordered := make([]ImportMatch, 0, len(matches))
for _, match := range matches {
ordered = append(ordered, match)
}
sort.Slice(ordered, func(i, j int) bool {
if ordered[i].PURL != ordered[j].PURL {
return ordered[i].PURL < ordered[j].PURL
}
left := ordered[i].Provided
right := ordered[j].Provided
if left.Language != right.Language {
return left.Language < right.Language
}
if left.Name != right.Name {
return left.Name < right.Name
}
if left.Kind != right.Kind {
return left.Kind < right.Kind
}
if left.Match != right.Match {
return left.Match < right.Match
}
return left.Separator < right.Separator
})
return ImportResult{
Language: language,
Name: name,
Matches: ordered,
Diagnostics: mergeDiagnostics(project.Diagnostics),
}
}
type importMatchKey struct {
purl string
language string
name string
kind string
match MatchMode
separator string
caseInsensitive bool
}