github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/name_interface.go (about)

     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  
    15  package blueprint
    16  
    17  import (
    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
    29  type ModuleGroup struct {
    30  	*moduleGroup
    31  }
    32  
    33  func (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.
    42  type Namespace interface {
    43  	namespace(Namespace)
    44  }
    45  type NamespaceMarker struct {
    46  }
    47  
    48  func (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
    53  type 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
    79  type NamespaceContext interface {
    80  	ModulePath() string
    81  }
    82  
    83  type namespaceContextImpl struct {
    84  	modulePath string
    85  }
    86  
    87  func newNamespaceContext(moduleInfo *moduleInfo) (ctx NamespaceContext) {
    88  	return &namespaceContextImpl{moduleInfo.pos.Filename}
    89  }
    90  
    91  func (ctx *namespaceContextImpl) ModulePath() string {
    92  	return ctx.modulePath
    93  }
    94  
    95  // a SimpleNameInterface just stores all modules in a map based on name
    96  type SimpleNameInterface struct {
    97  	modules map[string]ModuleGroup
    98  }
    99  
   100  func NewSimpleNameInterface() *SimpleNameInterface {
   101  	return &SimpleNameInterface{
   102  		modules: make(map[string]ModuleGroup),
   103  	}
   104  }
   105  
   106  func (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[0].pos),
   113  		}
   114  	}
   115  
   116  	s.modules[name] = group
   117  
   118  	return nil, []error{}
   119  }
   120  
   121  func (s *SimpleNameInterface) ModuleFromName(moduleName string, namespace Namespace) (group ModuleGroup, found bool) {
   122  	group, found = s.modules[moduleName]
   123  	return group, found
   124  }
   125  
   126  func (s *SimpleNameInterface) Rename(oldName string, newName string, namespace Namespace) (errs []error) {
   127  	existingGroup, exists := s.modules[newName]
   128  	if exists {
   129  		errs = append(errs,
   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[0].pos),
   134  		)
   135  		return errs
   136  	}
   137  
   138  	group := s.modules[oldName]
   139  	s.modules[newName] = group
   140  	delete(s.modules, group.name)
   141  	group.name = newName
   142  	return []error{}
   143  }
   144  
   145  func (s *SimpleNameInterface) AllModules() []ModuleGroup {
   146  	groups := make([]ModuleGroup, 0, len(s.modules))
   147  	for _, group := range s.modules {
   148  		groups = append(groups, group)
   149  	}
   150  
   151  	duplicateName := ""
   152  	less := func(i, j int) bool {
   153  		if groups[i].name == groups[j].name {
   154  			duplicateName = groups[i].name
   155  		}
   156  		return groups[i].name < groups[j].name
   157  	}
   158  	sort.Slice(groups, less)
   159  	if duplicateName != "" {
   160  		// It is permitted to have two moduleGroup's with the same name, but not within the same
   161  		// Namespace. The SimpleNameInterface should catch this in NewModule, however, so this
   162  		// should never happen.
   163  		panic(fmt.Sprintf("Duplicate moduleGroup name %q", duplicateName))
   164  	}
   165  	return groups
   166  }
   167  
   168  func (s *SimpleNameInterface) MissingDependencyError(depender string, dependerNamespace Namespace, dependency string) (err error) {
   169  	return fmt.Errorf("%q depends on undefined module %q", depender, dependency)
   170  }
   171  
   172  func (s *SimpleNameInterface) GetNamespace(ctx NamespaceContext) Namespace {
   173  	return nil
   174  }
   175  
   176  func (s *SimpleNameInterface) UniqueName(ctx NamespaceContext, name string) (unique string) {
   177  	return name
   178  }