-
Notifications
You must be signed in to change notification settings - Fork 304
/
parser.go
154 lines (131 loc) · 4.3 KB
/
parser.go
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package parser
import (
"sync"
"golang.org/x/exp/slices"
"encr.dev/v2/internals/parsectx"
"encr.dev/v2/internals/pkginfo"
"encr.dev/v2/internals/scan"
"encr.dev/v2/internals/schema"
"encr.dev/v2/parser/apis"
"encr.dev/v2/parser/apis/api"
"encr.dev/v2/parser/apis/authhandler"
"encr.dev/v2/parser/apis/servicestruct"
"encr.dev/v2/parser/infra/caches"
"encr.dev/v2/parser/infra/config"
"encr.dev/v2/parser/infra/crons"
"encr.dev/v2/parser/infra/metrics"
"encr.dev/v2/parser/infra/pubsub"
"encr.dev/v2/parser/infra/secrets"
"encr.dev/v2/parser/infra/sqldb"
"encr.dev/v2/parser/resource"
"encr.dev/v2/parser/resource/resourceparser"
"encr.dev/v2/parser/resource/usage"
)
func NewParser(c *parsectx.Context) *Parser {
loader := pkginfo.New(c)
schemaParser := schema.NewParser(c, loader)
return &Parser{
c: c,
loader: loader,
schemaParser: schemaParser,
registry: resourceparser.NewRegistry(allParsers),
usageResolver: newUsageResolver(),
}
}
type Parser struct {
c *parsectx.Context
loader *pkginfo.Loader
schemaParser *schema.Parser
registry *resourceparser.Registry
usageResolver *usage.Resolver
}
func (p *Parser) MainModule() *pkginfo.Module {
return p.loader.MainModule()
}
func (p *Parser) RuntimeModule() *pkginfo.Module {
return p.loader.RuntimeModule()
}
// Parse parses the given application for uses of the Encore API Framework
// and the Encore infrastructure SDK.
func (p *Parser) Parse() *Result {
var (
mu sync.Mutex
pkgs []*pkginfo.Package
resources []resource.Resource
binds []resource.Bind
)
scan.ProcessModule(p.c.Errs, p.loader, p.c.MainModuleDir, func(pkg *pkginfo.Package) {
if pkg.Name == "main" {
// Ignore main packages that aren't the main package we're building, if any.
if mainPkg, ok := p.c.Build.MainPkg.Get(); !ok || pkg.ImportPath != mainPkg {
return
}
}
pass := &resourceparser.Pass{
Context: p.c,
SchemaParser: p.schemaParser,
Pkg: pkg,
}
interested := p.registry.InterestedParsers(pkg)
for _, p := range interested {
p.Run(pass)
}
mu.Lock()
pkgs = append(pkgs, pkg)
resources = append(resources, pass.Resources()...)
binds = append(binds, pass.Binds()...)
mu.Unlock()
})
// Sort resources by package and position so the array is stable between runs
// as we process modules in parallel we can't rely on the order of the
// resources being stable coming into this function.
slices.SortFunc(resources, func(a, b resource.Resource) bool {
if a.Package() != b.Package() {
return a.Package().FSPath < b.Package().FSPath
}
return a.Pos() < b.Pos()
})
// Then sort the binds binds
slices.SortFunc(binds, func(a, b resource.Bind) bool {
if a.Package() != b.Package() {
return a.Package().FSPath < b.Package().FSPath
}
return a.Pos() < b.Pos()
})
// Finally, sort the packages
slices.SortFunc(pkgs, func(a, b *pkginfo.Package) bool {
return a.FSPath < b.FSPath
})
// Because we've ordered pkgs and binds, usageExprs will be stable
usageExprs := usage.ParseExprs(p.schemaParser, pkgs, binds)
// Add the implicit sqldb usages.
usageExprs = append(usageExprs, sqldb.ComputeImplicitUsage(p.c.Errs, pkgs, binds)...)
return computeResult(p.c.Errs, p.MainModule(), p.usageResolver, pkgs, resources, binds, usageExprs)
}
// allParsers are all the resource parsers we support.
var allParsers = []*resourceparser.Parser{
apis.Parser,
caches.ClusterParser,
caches.KeyspaceParser,
config.LoadParser,
crons.JobParser,
metrics.MetricParser,
pubsub.TopicParser,
pubsub.SubscriptionParser,
secrets.SecretsParser,
sqldb.DatabaseParser,
sqldb.NamedParser,
}
func newUsageResolver() *usage.Resolver {
r := usage.NewResolver()
// Infrastructure SDK
usage.RegisterUsageResolver[*caches.Keyspace](r, caches.ResolveKeyspaceUsage)
usage.RegisterUsageResolver[*config.Load](r, config.ResolveConfigUsage)
usage.RegisterUsageResolver[*pubsub.Topic](r, pubsub.ResolveTopicUsage)
usage.RegisterUsageResolver[*sqldb.Database](r, sqldb.ResolveDatabaseUsage)
// API Framework
usage.RegisterUsageResolver[*api.Endpoint](r, api.ResolveEndpointUsage)
usage.RegisterUsageResolver[*authhandler.AuthHandler](r, authhandler.ResolveAuthHandlerUsage)
usage.RegisterUsageResolver[*servicestruct.ServiceStruct](r, servicestruct.ResolveServiceStructUsage)
return r
}