Documentation ¶
Index ¶
- func VerifyFactory(f interface{}) error
- func VerifyMiddleware(m interface{}) error
- type CycleError
- type Dependencies
- type Dependency
- type Factory
- type LoadedDependency
- type Loader
- type LoaderBasic
- type Middleware
- type Orchestrator
- type ReflectedFactory
- type ReflectedMiddleware
- type ReflectedMiddlewareFactory
- type Registry
- type Selector
- type SelectorBasic
- type Sorter
- type SorterTopological
- type Type
- type Validator
- type ValidatorChain
- type ValidatorCycle
- type ValidatorKind
- type ValidatorMissing
- type ValidatorTypes
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func VerifyFactory ¶ added in v0.4.0
func VerifyFactory(f interface{}) error
VerifyFactory checks if a given value implements the factory Protocol.
func VerifyMiddleware ¶ added in v0.4.0
func VerifyMiddleware(m interface{}) error
VerifyMiddleware checks that a given function implements the middleware protocol.
Types ¶
type CycleError ¶ added in v0.2.0
type CycleError struct { // Cycle contains the relevant cycle. Each dependency will contain one or // more implementations that are relevant to the cycle. Cycle []Dependency }
CycleError is emitted from any component that detects a dependency cycle.
func (CycleError) Error ¶ added in v0.2.0
func (e CycleError) Error() string
type Dependencies ¶ added in v0.2.0
type Dependencies []Dependency
Dependencies is a utility wrapper for a slice of Dependency instances that adds some quality of life features for managing the slice.
func (Dependencies) DeepCopy ¶ added in v0.2.0
func (ds Dependencies) DeepCopy() Dependencies
DeepCopy returns a copy of all dependencies and all implementations.
func (Dependencies) Get ¶ added in v0.2.0
func (ds Dependencies) Get(t reflect.Type) (Dependency, bool)
Get a dependency by type.
func (Dependencies) ShallowCopy ¶ added in v0.2.0
func (ds Dependencies) ShallowCopy() Dependencies
ShallowCopy returns a copy of all the dependencies but without any of the implementations.
type Dependency ¶
type Dependency struct { Type Type Provides reflect.Type Implementations []*ReflectedFactory Middleware []*ReflectedMiddlewareFactory }
Dependency is a data container used to represent the state of dependency graph nodes. The specific rules used to validate and load each Dependency are determiend by the assigned DependencyType.
type Factory ¶ added in v0.4.0
type Factory interface{}
Factory is intentionally empty and exists only for documentation purposes.
The factory protocol would generally be implemented using generics in other languages but is, instead, defined as documentation and enforced through reflection in Go.
The protocol defines an instance of a factory in the "factory function" sense. Each factory must define two methods: Config() and Make(). The pseudo-interface for these methods is:
type Protocol interface{ Config(ctx context.Context) *C Make(ctx context.Context, conf *C) (T, error) }
In this definition, `C` refers to a user defined struct that will act as a data container for configuration data. All input values needed by the `Make` method to construct an intance must be provided by the contents of `C`. The output of the `Config` method must be a non-nil pointer to an instance of `C` that contains any default values for configuration fields.
`T` refers to another user defined type that is being created by the factory. `T` may be defined as any valid Go type.
When creating factories, both `C` and `T` may be named anything and do not need to be called `C` and `T`. These names are merely placeholders. The important thing is that the output of `Config` is accepted as the seceond argument of `Make`. For example, here is a contrived implementation to help visualize:
type Configuration struct{ Timeout time.Duration } type Factory struct {} func(*Factory) Config(ctx context.Context) *Configuration { return &Configuration{Timeout: 5*time.Second} } func(*Factory) New(ctx context.Context, conf *Configuration) (*http.Client, error) { return &http.Client{Timeout: conf.Timeout, Transport: http.DefaultTransport}, nil }
In the above example, the `C` placeholder is now `Configuration` and the `T` placeholder is now `*http.Client`.
type LoadedDependency ¶
LoadedDependency is a container for an evaluated dependency. The Type and Provides values match the original Dependency container and the Value field contains the rendered instance. Extensions types will have a slice in the Value slot while Driver types will have a single instance. All elements of Value should be decorated by any selected middleware.
type Loader ¶
type Loader interface {
Load(ctx context.Context, ds []Dependency) ([]LoadedDependency, error)
}
Loader implementations construct instances of all the requested dependencies.
type LoaderBasic ¶ added in v0.2.0
type LoaderBasic struct{}
LoaderBasic creates instances of the various factory outputs by passing in their default configuration. You almost certainly need to replace this with a configuration loading system. This is provided mostly as a reference implementation to demonstrate how loading might be done.
func (*LoaderBasic) Load ¶ added in v0.2.0
func (ld *LoaderBasic) Load(ctx context.Context, ds []Dependency) ([]LoadedDependency, error)
Load all dependency implementations in the order provided.
type Middleware ¶ added in v0.4.0
type Middleware interface{}
Middleware is intentionally empty and exists only for documentation purposes.
The middleware protocol would generally be implemented using generics in other languages but is, instead, defined as documentation and enforced through reflection in Go.
The protocol defines an instance of a middleware as a function that accepts a type and returns a value of an equivalent type. The explicit purpose of middleware functions is to wrap types with additional functionality. Each middleware may implement any functionality such as adding logging, telemetry, automated retries, or HTTP request input validation, etc. Each middleware function may defined in one of several forms:
func(T) T func(T) (T, erro) func(context.Context, T) T func(context.Context, T) (T, error)
In these definitions `T` refers to any type that is being wrapped by the middleware. Middleware functions may optionally return an error if there is a possibility of failure or accept a context if there is need for cancellation support.
It is generally expected that the input and output types will be exactly the same. That is, if a middleware accepts `http.RoundTripper` as in the method input then it would almost always return `http.RoundTripper` as the output. However, it is possible to return a different type for the output so long as it is convertible to the input type. For example, the method could appear as:
func(http.RoundTripper) MyRoundTripperWrapper
so long as the `MyRoundTripperWrapper` type implements the `http.RoundTripper` interface. This allows middleware functions to return concrete types but remain compatible with other middleware that may only accept and return the interface type.
type Orchestrator ¶ added in v0.2.0
Orchestrator wraps the four major components of the library (Validator, Selector, Sorter, Loader) and manages the execution of each for a given input set of dependencies.
func (*Orchestrator) Orchestrate ¶ added in v0.2.0
func (or *Orchestrator) Orchestrate(ctx context.Context, ds []Dependency) ([]LoadedDependency, error)
Orchestrate processes the given dependency set by passing it through the Validator, Selector, Sorter, and Loader in that order. Any errors encountered interrupt the process.
type ReflectedFactory ¶ added in v0.4.0
type ReflectedFactory struct { // Instance is a reference to the active factory instance. Instance reflect.Value // Requirements is a map of field names field values that represent the // requirements of the factory. Requirements map[string]reflect.Value // ConfigType is a reference to the output type of the Config() method. ConfigType reflect.Type // ConfigFn is a reference to the Config() method. ConfigFn reflect.Value // MakeType is a reference to the output type of the Make() method. MakeType reflect.Type // MakeFn is a reference to the Make() method. MakeFn reflect.Value }
ReflectedFactory is a instance of the Factory Protocol that has the key components extracted for convenience.
func ReflectFactory ¶ added in v0.4.0
func ReflectFactory(f interface{}) (*ReflectedFactory, error)
ReflectFactory wraps the given factory.
func (*ReflectedFactory) Config ¶ added in v0.4.0
func (f *ReflectedFactory) Config(ctx context.Context) interface{}
Config calls the reflected Config method.
type ReflectedMiddleware ¶ added in v0.4.0
type ReflectedMiddleware struct { // Instance is a reference to the original middleware function. Instance reflect.Value // Input is the type accepted by the middleware function Input reflect.Type // Output is the type returned by the middleware function. This will almost // always be identical to Input but can vary under specialized cases. For // example, a middleware may accept an interface type, // such as http.RoundTripper, and return a concrete type, such as // MyRoundTripperWrapper, that implements the original http.RoundTripper // interface. Output reflect.Type // AcceptsContext indicates if the middleware method accepts a // context.Context as the first argument. AcceptsContext bool // ReturnsError indicates if the middleware method returns an error as the // second argument. ReturnsError bool }
func ReflectMiddleware ¶ added in v0.4.0
func ReflectMiddleware(m interface{}) (*ReflectedMiddleware, error)
type ReflectedMiddlewareFactory ¶ added in v0.4.0
type ReflectedMiddlewareFactory struct { *ReflectedFactory // Reference exposes the type information associated with the middleware // that is produced by the factory. It should never be invoked as a // function because it will be an empty reference to type information and // will panic if called. Reference *ReflectedMiddleware }
func ReflectMiddlewareFactory ¶ added in v0.4.0
func ReflectMiddlewareFactory(f interface{}) (*ReflectedMiddlewareFactory, error)
func (*ReflectedMiddlewareFactory) Make ¶ added in v0.4.0
func (f *ReflectedMiddlewareFactory) Make(ctx context.Context, c interface{}) (*ReflectedMiddleware, error)
Make calls the reflected Make method and then reflects the middleware.
type Registry ¶ added in v0.4.0
type Registry interface { // Add a dependency or middleware to the registry. The value may be any // valid factory or middleware factory depending on the type value given. // The output type of the given factory will become the identifying type // that other dependency may require. // // Note for implementations: TypeMiddleware may be given but must never be // set as the value for a Dependency instance's Type field. All Dependency // instances must be either TypeDriver or TypeExtension. TypeMiddleware is // supported only as a convenience for the user. Add(dt Type, f interface{}) error // AddAs behaves like Add except that the identifying type is no longer // assumed from the factory output and is, instead, taken from the given // `t` value. The correct way to use this is the use `new()` to generate // an instance of the target type. For example, if the factory, `f`, outputs // a `MyRoundTripper` type but it should appear as an option for anything // that requires `http.RoundTripper` then the call would look like: // // r.AddAs(depo.Driver, new(MyFactory), new(http.RoundTripper)) AddAs(dt Type, f interface{}, t interface{}) error // Dependencies returns the current registry as a list of Dependency // instances. Dependencies() []Dependency }
Registry is used to declare dependencies, implementations, and middleware.
func NewRegistry ¶ added in v0.4.0
func NewRegistry() Registry
NewRegistry generates an instance of the default registry implementation.
type Selector ¶
type Selector interface {
Select(ctx context.Context, ds []Dependency) ([]Dependency, error)
}
Selector filters the implementations of each Dependency to only those that will be loaded at runtime. Any selection process may be used so long as the Type rules are maintained such that each TypeDriver has exactly one implementation, and each TypeExtension has zero or more implementations. Likewise, zero ore more TypeMiddleware may be enabled. Whichever remains in the implementations lists after this component processes them will be loaded.
type SelectorBasic ¶ added in v0.2.0
type SelectorBasic struct{}
SelectorBasic implements Selector by choosing the first implementation in the set for each driver, enabling all extensions, and enabling all middleware.
func (*SelectorBasic) Select ¶ added in v0.2.0
func (s *SelectorBasic) Select(ctx context.Context, ds []Dependency) ([]Dependency, error)
type Sorter ¶
type Sorter interface {
Sort(ctx context.Context, ds []Dependency) ([]Dependency, error)
}
Sorter implementations arrange the dependency set into an order that can be loaded. The final ordering must be such that each dependency in the result comes after all dependencies required by its implementations.
type SorterTopological ¶
type SorterTopological struct{}
SorterTopological orders the results such that any dependency appears only after all dependencies of all its implemenations.
func (*SorterTopological) Sort ¶
func (s *SorterTopological) Sort(ctx context.Context, ds []Dependency) ([]Dependency, error)
type Type ¶ added in v0.4.0
type Type string
Type is used to distinguish between expected behaviors from different kinds of system dependencies.
const ( // TypeDriver has exactly one implementation active at runtime. TypeDriver Type = "DRIVER" // TypeExtension has zero or more implementations active at runtime. TypeExtension Type = "LIST" // TypeMiddleware has zero ore more active implementations for // any given dependency. They are either applied to the driver selection or // to all enabled list elements. TypeMiddleware Type = "MIDDLEWARE" )
type Validator ¶
type Validator interface {
Validate(ctx context.Context, ds []Dependency) error
}
Validator applies one or more rules to the given set of Dependency instances to determine if they are a valid set when used together.
func NewValidator ¶
func NewValidator() Validator
NewValidator generates the default validator chain that includes all built in validators.
type ValidatorChain ¶
type ValidatorChain []Validator
ValidatorChain is a sequence of validators to run. If any validator errors then the chain is stopped and the error returned.
func (ValidatorChain) Validate ¶
func (c ValidatorChain) Validate(ctx context.Context, ds []Dependency) error
Validate against all included validators.
type ValidatorCycle ¶
type ValidatorCycle struct{}
ValidatorCycle asserts that there are no dependency cycles in the given set.
func (*ValidatorCycle) Validate ¶
func (v *ValidatorCycle) Validate(ctx context.Context, ds []Dependency) error
Validate each dependency for cycles.
type ValidatorKind ¶
type ValidatorKind struct{}
ValidatorKind enforces the rules for all known Type values. For example, this asserts that there at least one implementation per TypeDriver.
func (*ValidatorKind) Validate ¶
func (v *ValidatorKind) Validate(ctx context.Context, ds []Dependency) error
Validate each dependency in the set against the rules for its kind.
type ValidatorMissing ¶
type ValidatorMissing struct{}
ValidatorMissing enforces that all implementation requirements are represented in the set of dependencies.
func (*ValidatorMissing) Validate ¶
func (v *ValidatorMissing) Validate(ctx context.Context, ds []Dependency) error
Validate the dependency set against missing dependencies.
type ValidatorTypes ¶
type ValidatorTypes struct{}
ValidatorTypes enforces that every Factory requirement can be satisfied and that any registered middleware are compatible with registered implementations.
func (*ValidatorTypes) Validate ¶
func (v *ValidatorTypes) Validate(ctx context.Context, ds []Dependency) error