109 lines
3.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
// Package factory contains a "factory" pattern based on argmapper.
//
// A Factory can be used to register factory methods to create some predefined
// type or interface implementation. These functions are argmapper functions so
// converters and so on can be used as part of instantiation.
package factory
import (
"fmt"
"reflect"
"github.com/hashicorp/go-argmapper"
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/dynamic"
)
// Factory keeps track of named dependency-injected factory functions to
// create an implementation of an interface.
type Factory struct {
iface reflect.Type
funcs map[string]*argmapper.Func
}
// New creates a Factory for the interface iface. The parameter
// iface should be a nil pointer to the interface type. Example: (*iface)(nil).
func New(iface interface{}) (*Factory, error) {
// Get the interface type
it := reflect.TypeOf(iface)
if k := it.Kind(); k != reflect.Ptr {
return nil, fmt.Errorf("iface must be a pointer to an interface, got %s", k)
}
it = it.Elem()
if k := it.Kind(); k != reflect.Interface {
return nil, fmt.Errorf("iface must be a pointer to an interface, got %s", k)
}
return &Factory{iface: it, funcs: make(map[string]*argmapper.Func)}, nil
}
// Register registers a factory function named name for the interface.
//
// This will error if the function given doesn't result in a single non-error
// output that implements the interface registered with this factory. The
// function return signature can be: (T) or (T, error) where T implements
// the interface type for this factory.
//
// T is allowed to be a literal interface{} type. In this case, it is the
// callers responsibility to ensure the result is the proper type.
//
// fn may take any number and types of inputs. It is the callers responsibilty
// when using Func and Call to pass in the required parameters.
func (f *Factory) Register(name string, fn interface{}) error {
ff, err := argmapper.NewFunc(fn,
argmapper.Logger(dynamic.Logger))
if err != nil {
return err
}
outputs := ff.Output().Values()
if len(outputs) != 1 {
return fmt.Errorf("factory functions should have exactly one output: the implementation")
}
// We allow "interface{}" to pass through, in which case we trust that
// the callback is generating the correct type.
typ := outputs[0].Type
if typ != ifaceType && !typ.Implements(f.iface) {
return fmt.Errorf("factory output should implement interface: %s", f.iface)
}
f.funcs[name] = ff
return nil
}
// Func returns the factory function named name. This can then be used to
// call and instantiate the factory interface type.
func (f *Factory) Func(name string) *argmapper.Func {
return f.funcs[name]
}
// Registered returns the names registered with this factory.
func (f *Factory) Registered() []string {
result := make([]string, 0, len(f.funcs))
for k := range f.funcs {
result = append(result, k)
}
return result
}
// Copy returns a copy of Factory. Any registrations on the copy will not
// reflect the original and vice versa.
func (f *Factory) Copy() *Factory {
// Copy
f2 := *f
// Build new funcs
f2.funcs = map[string]*argmapper.Func{}
for k, v := range f.funcs {
f2.funcs[k] = v
}
return &f2
}
var ifaceType = reflect.TypeOf((*interface{})(nil)).Elem()