1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package blueprint
16
17import (
18	"fmt"
19	"sort"
20)
21
22// This file exposes the logic of locating a module via a query string, to enable
23// other projects to override it if desired.
24// The default name resolution implementation, SimpleNameInterface,
25// just treats the query string as a module name, and does a simple map lookup.
26
27// A ModuleGroup just points to a moduleGroup to allow external packages to refer
28// to a moduleGroup but not use it
29type ModuleGroup struct {
30	*moduleGroup
31}
32
33func (h *ModuleGroup) String() string {
34	return h.moduleGroup.name
35}
36
37// The Namespace interface is just a marker interface for usage by the NameInterface,
38// to allow a NameInterface to specify that a certain parameter should be a Namespace.
39// In practice, a specific NameInterface will expect to only give and receive structs of
40// the same concrete type, but because Go doesn't support generics, we use a marker interface
41// for a little bit of clarity, and expect implementers to do typecasting instead.
42type Namespace interface {
43	namespace(Namespace)
44}
45type NamespaceMarker struct {
46}
47
48func (m *NamespaceMarker) namespace(Namespace) {
49}
50
51// A NameInterface tells how to locate modules by name.
52// There should only be one name interface per Context, but potentially many namespaces
53type NameInterface interface {
54	// Gets called when a new module is created
55	NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error)
56
57	// Finds the module with the given name
58	ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool)
59
60	// Returns an error indicating that the given module could not be found.
61	// The error contains some diagnostic information about where the dependency can be found.
62	MissingDependencyError(depender string, dependerNamespace Namespace, depName string) (err error)
63
64	// Rename
65	Rename(oldName string, newName string, namespace Namespace) []error
66
67	// Returns all modules in a deterministic order.
68	AllModules() []ModuleGroup
69
70	// gets the namespace for a given path
71	GetNamespace(ctx NamespaceContext) (namespace Namespace)
72
73	// returns a deterministic, unique, arbitrary string for the given name in the given namespace
74	UniqueName(ctx NamespaceContext, name string) (unique string)
75}
76
77// A NamespaceContext stores the information given to a NameInterface to enable the NameInterface
78// to choose the namespace for any given module
79type NamespaceContext interface {
80	ModulePath() string
81}
82
83type namespaceContextImpl struct {
84	modulePath string
85}
86
87func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) {
88	return &namespaceContextImpl{moduleInfo.pos.Filename}
89}
90
91func (ctx *namespaceContextImpl) ModulePath() string {
92	return ctx.modulePath
93}
94
95// a SimpleNameInterface just stores all modules in a map based on name
96type SimpleNameInterface struct {
97	modules map[string]ModuleGroup
98}
99
100func NewSimpleNameInterface() *SimpleNameInterface {
101	return &SimpleNameInterface{
102		modules: make(map[string]ModuleGroup),
103	}
104}
105
106func (s *SimpleNameInterface) NewModule(ctx NamespaceContext, group ModuleGroup, module Module) (namespace Namespace, err []error) {
107	name := group.name
108	if group, present := s.modules[name]; present {
109		return nil, []error{
110			// seven characters at the start of the second line to align with the string "error: "
111			fmt.Errorf("module %q already defined\n"+
112				"       %s <-- previous definition here", name, group.modules.firstModule().pos),
113		}
114	}
115
116	s.modules[name] = group
117
118	return nil, []error{}
119}
120
121func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
122	group, found = s.modules[moduleName]
123	return group, found
124}
125
126func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) {
127	existingGroup, exists := s.modules[newName]
128	if exists {
129		return []error{
130			// seven characters at the start of the second line to align with the string "error: "
131			fmt.Errorf("renaming module %q to %q conflicts with existing module\n"+
132				"       %s <-- existing module defined here",
133				oldName, newName, existingGroup.modules.firstModule().pos),
134		}
135	}
136
137	group, exists := s.modules[oldName]
138	if !exists {
139		return []error{fmt.Errorf("module %q to renamed to %q doesn't exist", oldName, newName)}
140	}
141	s.modules[newName] = group
142	delete(s.modules, group.name)
143	group.name = newName
144	return nil
145}
146
147func (s *SimpleNameInterface) AllModules() []ModuleGroup {
148	groups := make([]ModuleGroup, 0, len(s.modules))
149	for _, group := range s.modules {
150		groups = append(groups, group)
151	}
152
153	duplicateName := ""
154	less := func(i, j int) bool {
155		if groups[i].name == groups[j].name {
156			duplicateName = groups[i].name
157		}
158		return groups[i].name < groups[j].name
159	}
160	sort.Slice(groups, less)
161	if duplicateName != "" {
162		// It is permitted to have two moduleGroup's with the same name, but not within the same
163		// Namespace. The SimpleNameInterface should catch this in NewModule, however, so this
164		// should never happen.
165		panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName))
166	}
167	return groups
168}
169
170func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string) (err error) {
171	return fmt.Errorf("%q depends on undefined module %q", depender, dependency)
172}
173
174func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace {
175	return nil
176}
177
178func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) {
179	return name
180}
181