forked from google/blueprint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
name_interface.go
180 lines (149 loc) · 5.97 KB
/
name_interface.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package blueprint
import (
"fmt"
"sort"
)
// This file exposes the logic of locating a module via a query string, to enable
// other projects to override it if desired.
// The default name resolution implementation, SimpleNameInterface,
// just treats the query string as a module name, and does a simple map lookup.
// A ModuleGroup just points to a moduleGroup to allow external packages to refer
// to a moduleGroup but not use it
type ModuleGroup struct {
*moduleGroup
}
func (h *ModuleGroup) String() string {
return h.moduleGroup.name
}
// The Namespace interface is just a marker interface for usage by the NameInterface,
// to allow a NameInterface to specify that a certain parameter should be a Namespace.
// In practice, a specific NameInterface will expect to only give and receive structs of
// the same concrete type, but because Go doesn't support generics, we use a marker interface
// for a little bit of clarity, and expect implementers to do typecasting instead.
type Namespace interface {
namespace(Namespace)
}
type NamespaceMarker struct {
}
func (m *NamespaceMarker) namespace(Namespace) {
}
// A NameInterface tells how to locate modules by name.
// There should only be one name interface per Context, but potentially many namespaces
type NameInterface interface {
// Gets called when a new module is created
NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error)
// Finds the module with the given name
ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
// Returns an error indicating that the given module could not be found.
// The error contains some diagnostic information about where the dependency can be found.
MissingDependencyError(depender string, dependerNamespace Namespace, depName string) (err error)
// Rename
Rename(oldName string, newName string, namespace Namespace) []error
// Returns all modules in a deterministic order.
AllModules() []ModuleGroup
// gets the namespace for a given path
GetNamespace(ctx NamespaceContext) (namespace Namespace)
// returns a deterministic, unique, arbitrary string for the given name in the given namespace
UniqueName(ctx NamespaceContext, name string) (unique string)
}
// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface
// to choose the namespace for any given module
type NamespaceContext interface {
ModulePath() string
}
type namespaceContextImpl struct {
modulePath string
}
func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) {
return &namespaceContextImpl{moduleInfo.pos.Filename}
}
func (ctx *namespaceContextImpl) ModulePath() string {
return ctx.modulePath
}
// a SimpleNameInterface just stores all modules in a map based on name
type SimpleNameInterface struct {
modules map[string]ModuleGroup
}
func NewSimpleNameInterface() *SimpleNameInterface {
return &SimpleNameInterface{
modules: make(map[string]ModuleGroup),
}
}
func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) {
name := group.name
if group, present := s.modules[name]; present {
return nil, []error{
// seven characters at the start of the second line to align with the string "error: "
fmt.Errorf("module %q already defined\n"+
" %s <-- previous definition here", name, group.modules[0].pos),
}
}
s.modules[name] = group
return nil, []error{}
}
func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
group, found = s.modules[moduleName]
return group, found
}
func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) {
existingGroup, exists := s.modules[newName]
if exists {
return []error{
// seven characters at the start of the second line to align with the string "error: "
fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+
" %s <-- existing module defined here",
oldName, newName, existingGroup.modules[0].pos),
}
}
group, exists := s.modules[oldName]
if !exists {
return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)}
}
s.modules[newName] = group
delete(s.modules, group.name)
group.name = newName
return nil
}
func (s *SimpleNameInterface) AllModules() []ModuleGroup {
groups := make([]ModuleGroup, 0, len(s.modules))
for _, group := range s.modules {
groups = append(groups, group)
}
duplicateName := ""
less := func(i, j int) bool {
if groups[i].name == groups[j].name {
duplicateName = groups[i].name
}
return groups[i].name < groups[j].name
}
sort.Slice(groups, less)
if duplicateName != "" {
// It is permitted to have two moduleGroup's with the same name, but not within the same
// Namespace. The SimpleNameInterface should catch this in NewModule, however, so this
// should never happen.
panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName))
}
return groups
}
func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string) (err error) {
return fmt.Errorf("%q depends on undefined module %q", depender, dependency)
}
func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace {
return nil
}
func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) {
return name
}