From 5aca612c4358bd4c6f0a5e440ee6d9fdecc1a8b5 Mon Sep 17 00:00:00 2001 From: Matthias Friedrich <1573457+matzefriedrich@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:38:49 +0200 Subject: [PATCH] Pre release/code documentation (#29) * Adds documentation comments to the pkg/types package * Adds documentation comments to the pkg/resolving package * Adds documentation comments to the pkg/registration package --- pkg/registration/activator.go | 4 + pkg/registration/dependency.go | 1 + .../named_service_registration.go | 2 + pkg/registration/registry.go | 10 ++ pkg/registration/registry_accessor.go | 3 + pkg/registration/service_registration.go | 9 ++ pkg/registration/service_registration_list.go | 6 ++ pkg/resolving/activate.go | 8 +- pkg/resolving/named_service_resolver.go | 2 + pkg/resolving/resolver.go | 6 ++ pkg/resolving/resolver_options.go | 1 + pkg/resolving/scope.go | 1 + pkg/types/dependency_error.go | 4 + pkg/types/parsley_error.go | 12 +++ pkg/types/reflection_error.go | 6 +- pkg/types/registry_error.go | 18 +++- pkg/types/resolver_error.go | 37 ++++++-- pkg/types/service_type.go | 8 ++ pkg/types/types.go | 94 ++++++++++++++++++- 19 files changed, 217 insertions(+), 15 deletions(-) diff --git a/pkg/registration/activator.go b/pkg/registration/activator.go index 9d14d27..cf7a7df 100644 --- a/pkg/registration/activator.go +++ b/pkg/registration/activator.go @@ -6,6 +6,7 @@ import ( "reflect" ) +// CreateServiceActivatorFrom creates a service activator function for a given instance of type T. func CreateServiceActivatorFrom[T any](instance T) (func() T, error) { if internal.IsNil(instance) { return nil, types.NewRegistryError(types.ErrorInstanceCannotBeNil) @@ -25,6 +26,9 @@ func CreateServiceActivatorFrom[T any](instance T) (func() T, error) { return instanceFunc, nil } +// RegisterInstance registers an instance of type T. A registered instance behaves like a service registration with +// a singleton lifetime scope. See https://matzefriedrich.github.io/parsley-docs/registration/register-instances/ for +// further information. func RegisterInstance[T any](registry types.ServiceRegistry, instance T) error { instanceFunc, err := CreateServiceActivatorFrom[T](instance) if err != nil { diff --git a/pkg/registration/dependency.go b/pkg/registration/dependency.go index ffef1a9..8bd0e3c 100644 --- a/pkg/registration/dependency.go +++ b/pkg/registration/dependency.go @@ -15,6 +15,7 @@ type dependencyInfo struct { var _ types.DependencyInfo = &dependencyInfo{} +// NewDependencyInfo creates a new instance of types.DependencyInfo with the provided service registration, instance, and parent dependency. func NewDependencyInfo(registration types.ServiceRegistration, instance interface{}, consumer types.DependencyInfo) types.DependencyInfo { return &dependencyInfo{ registration: registration, diff --git a/pkg/registration/named_service_registration.go b/pkg/registration/named_service_registration.go index 844f26e..66aad6a 100644 --- a/pkg/registration/named_service_registration.go +++ b/pkg/registration/named_service_registration.go @@ -4,8 +4,10 @@ import ( "github.com/matzefriedrich/parsley/pkg/types" ) +// NamedServiceRegistrationFunc defines a function that returns a service name, its activator function, and its lifetime scope. This type supports the internal infrastructure. type NamedServiceRegistrationFunc func() (name string, activatorFunc any, scope types.LifetimeScope) +// NamedServiceRegistration registers a service with a specified name, activator function, and lifetime scope. func NamedServiceRegistration(name string, activatorFunc any, scope types.LifetimeScope) NamedServiceRegistrationFunc { return func() (string, any, types.LifetimeScope) { return name, activatorFunc, scope diff --git a/pkg/registration/registry.go b/pkg/registration/registry.go index 658f199..b7b2a89 100644 --- a/pkg/registration/registry.go +++ b/pkg/registration/registry.go @@ -10,14 +10,18 @@ type serviceRegistry struct { registrations map[types.ServiceKey]types.ServiceRegistrationList } +// RegisterTransient adds a transient service registration with the provided activator function. +// See https://matzefriedrich.github.io/parsley-docs/registration/register-constructor-functions/ for further information. func RegisterTransient(registry types.ServiceRegistry, activatorFunc any) error { return registry.Register(activatorFunc, types.LifetimeTransient) } +// RegisterScoped adds a scoped service registration with the provided activator function. func RegisterScoped(registry types.ServiceRegistry, activatorFunc any) error { return registry.Register(activatorFunc, types.LifetimeScoped) } +// RegisterSingleton adds a singleton service registration with the provided activator function. func RegisterSingleton(registry types.ServiceRegistry, activatorFunc any) error { return registry.Register(activatorFunc, types.LifetimeSingleton) } @@ -32,6 +36,7 @@ func (s *serviceRegistry) addOrUpdateServiceRegistrationListFor(serviceType type return list } +// Register adds a service registration with the provided activator function and lifetime scope. func (s *serviceRegistry) Register(activatorFunc any, lifetimeScope types.LifetimeScope) error { registration, err := CreateServiceRegistration(activatorFunc, lifetimeScope) @@ -49,6 +54,7 @@ func (s *serviceRegistry) Register(activatorFunc any, lifetimeScope types.Lifeti return nil } +// RegisterModule registers one or more modules with the service registry. func (s *serviceRegistry) RegisterModule(modules ...types.ModuleFunc) error { for _, m := range modules { err := m(s) @@ -59,11 +65,13 @@ func (s *serviceRegistry) RegisterModule(modules ...types.ModuleFunc) error { return nil } +// IsRegistered checks if a service type is registered in the service registry. func (s *serviceRegistry) IsRegistered(serviceType types.ServiceType) bool { _, found := s.registrations[serviceType.LookupKey()] return found } +// TryGetServiceRegistrations Tries to find all available service registrations for the given service type. func (s *serviceRegistry) TryGetServiceRegistrations(serviceType types.ServiceType) (types.ServiceRegistrationList, bool) { if s.IsRegistered(serviceType) == false { return nil, false @@ -75,6 +83,7 @@ func (s *serviceRegistry) TryGetServiceRegistrations(serviceType types.ServiceTy return nil, false } +// TryGetSingleServiceRegistration Tries to find a single service registration for the given service type. func (s *serviceRegistry) TryGetSingleServiceRegistration(serviceType types.ServiceType) (types.ServiceRegistration, bool) { list, found := s.TryGetServiceRegistrations(serviceType) if found && list.IsEmpty() == false { @@ -87,6 +96,7 @@ func (s *serviceRegistry) TryGetSingleServiceRegistration(serviceType types.Serv return nil, false } +// NewServiceRegistry creates a new types.ServiceRegistry instance. func NewServiceRegistry() types.ServiceRegistry { registrations := make(map[types.ServiceKey]types.ServiceRegistrationList) return &serviceRegistry{ diff --git a/pkg/registration/registry_accessor.go b/pkg/registration/registry_accessor.go index 7f775d0..63be0dd 100644 --- a/pkg/registration/registry_accessor.go +++ b/pkg/registration/registry_accessor.go @@ -8,6 +8,7 @@ type multiRegistryAccessor struct { registries []types.ServiceRegistryAccessor } +// TryGetSingleServiceRegistration attempts to find a single service registration for the given service type in multiple registries. func (m *multiRegistryAccessor) TryGetSingleServiceRegistration(serviceType types.ServiceType) (types.ServiceRegistration, bool) { for _, registry := range m.registries { registration, ok := registry.TryGetSingleServiceRegistration(serviceType) @@ -18,6 +19,7 @@ func (m *multiRegistryAccessor) TryGetSingleServiceRegistration(serviceType type return nil, false } +// TryGetServiceRegistrations tries to retrieve all service registrations for the given service type from multiple registries. func (m *multiRegistryAccessor) TryGetServiceRegistrations(serviceType types.ServiceType) (types.ServiceRegistrationList, bool) { for _, registry := range m.registries { registration, ok := registry.TryGetServiceRegistrations(serviceType) @@ -30,6 +32,7 @@ func (m *multiRegistryAccessor) TryGetServiceRegistrations(serviceType types.Ser var _ types.ServiceRegistryAccessor = &multiRegistryAccessor{} +// NewMultiRegistryAccessor creates a new ServiceRegistryAccessor that aggregates multiple registries. func NewMultiRegistryAccessor(registries ...types.ServiceRegistryAccessor) types.ServiceRegistryAccessor { serviceRegistries := make([]types.ServiceRegistryAccessor, 0) serviceRegistries = append(serviceRegistries, registries...) diff --git a/pkg/registration/service_registration.go b/pkg/registration/service_registration.go index e487c82..a234083 100644 --- a/pkg/registration/service_registration.go +++ b/pkg/registration/service_registration.go @@ -29,6 +29,7 @@ func newTypeInfo(t types.ServiceType) typeInfo { } } +// InvokeActivator calls the activator function with the provided parameters and returns the created service instance. func (s *serviceRegistration) InvokeActivator(params ...interface{}) (interface{}, error) { var values []reflect.Value if len(params) > 0 { @@ -45,10 +46,12 @@ func (s *serviceRegistration) InvokeActivator(params ...interface{}) (interface{ return serviceInstance.Interface(), nil } +// Id returns the unique identifier of this service registration. func (s *serviceRegistration) Id() uint64 { return s.id } +// SetId sets the unique identifier for the service registration. Returns an error if the id is already set. func (s *serviceRegistration) SetId(id uint64) error { if s.id != 0 { return errors.New("the id cannot be changed once set") @@ -57,6 +60,7 @@ func (s *serviceRegistration) SetId(id uint64) error { return nil } +// IsSame Returns true, if the current instance equals the given service registration instance. func (s *serviceRegistration) IsSame(other types.ServiceRegistration) bool { sr, ok := other.(*serviceRegistration) if ok { @@ -71,10 +75,12 @@ func (s *serviceRegistration) IsSame(other types.ServiceRegistration) bool { return false } +// LifetimeScope returns the lifetime scope of the service registration. func (s *serviceRegistration) LifetimeScope() types.LifetimeScope { return s.lifetimeScope } +// RequiredServiceTypes returns a slice of ServiceType representing the service dependencies. func (s *serviceRegistration) RequiredServiceTypes() []types.ServiceType { requiredTypes := make([]types.ServiceType, len(s.parameters)) for i, p := range s.parameters { @@ -83,10 +89,12 @@ func (s *serviceRegistration) RequiredServiceTypes() []types.ServiceType { return requiredTypes } +// ServiceType returns the service type of the service registration. func (s *serviceRegistration) ServiceType() types.ServiceType { return s.serviceType.t } +// String returns a string representation of the service registration, including the typename and its activator parameter types. func (s *serviceRegistration) String() string { buffer := strings.Builder{} @@ -99,6 +107,7 @@ func (s *serviceRegistration) String() string { return buffer.String() } +// CreateServiceRegistration creates a service registration instance from the given activator function and lifetime scope. func CreateServiceRegistration(activatorFunc any, lifetimeScope types.LifetimeScope) (types.ServiceRegistrationSetup, error) { value := reflect.ValueOf(activatorFunc) diff --git a/pkg/registration/service_registration_list.go b/pkg/registration/service_registration_list.go index 4d4fd00..8fed71c 100644 --- a/pkg/registration/service_registration_list.go +++ b/pkg/registration/service_registration_list.go @@ -13,16 +13,20 @@ type serviceRegistrationList struct { m sync.RWMutex } +// IsEmpty Returns true if the list contains no registrations, otherwise false. func (s *serviceRegistrationList) IsEmpty() bool { return len(s.registrations) == 0 } +// Registrations returns a slice of ServiceRegistration representing the current registrations in the list. func (s *serviceRegistrationList) Registrations() []types.ServiceRegistration { s.m.RLock() defer s.m.RUnlock() return s.registrations } +// AddRegistration adds a new service registration to the list. +// It returns an ErrorTypeAlreadyRegistered error if the registration already exists. func (s *serviceRegistrationList) AddRegistration(registration types.ServiceRegistrationSetup) error { s.m.Lock() @@ -44,12 +48,14 @@ func (s *serviceRegistrationList) AddRegistration(registration types.ServiceRegi return nil } +// Id returns the unique identifier of the service registration list. func (s *serviceRegistrationList) Id() uint64 { return s.id } var _ types.ServiceRegistrationList = &serviceRegistrationList{} +// NewServiceRegistrationList creates a new service registration list instance. func NewServiceRegistrationList(sequence core.ServiceIdSequence) types.ServiceRegistrationList { id := sequence.Next() return &serviceRegistrationList{ diff --git a/pkg/resolving/activate.go b/pkg/resolving/activate.go index 8dd0f0c..4035add 100644 --- a/pkg/resolving/activate.go +++ b/pkg/resolving/activate.go @@ -6,17 +6,21 @@ import ( "github.com/matzefriedrich/parsley/pkg/types" ) +// Activate attempts to create and return an instance of the requested type using the provided resolver. +// This method can be used to instantiate service objects of unregistered types. The specified activator function can +// have parameters to demand service instances for registered service types. +// See https://matzefriedrich.github.io/parsley-docs/resolving/resolve-live-services/ for further information. func Activate[T any](resolver types.Resolver, ctx context.Context, activatorFunc any, options ...types.ResolverOptionsFunc) (T, error) { var nilInstance T lifetimeScope := types.LifetimeTransient - registration, registrationErr := registration.CreateServiceRegistration(activatorFunc, lifetimeScope) + serviceRegistration, registrationErr := registration.CreateServiceRegistration(activatorFunc, lifetimeScope) if registrationErr != nil { return nilInstance, types.NewResolverError(types.ErrorCannotCreateInstanceOfUnregisteredType, types.WithCause(registrationErr)) } - serviceType := registration.ServiceType() + serviceType := serviceRegistration.ServiceType() resolveActivatorFuncOption := func(registry types.ServiceRegistry) error { return registry.Register(activatorFunc, lifetimeScope) } diff --git a/pkg/resolving/named_service_resolver.go b/pkg/resolving/named_service_resolver.go index 58313bb..089936b 100644 --- a/pkg/resolving/named_service_resolver.go +++ b/pkg/resolving/named_service_resolver.go @@ -5,8 +5,10 @@ import ( "github.com/matzefriedrich/parsley/pkg/types" ) +// NamedServiceResolverActivatorFunc defines a function for resolving named services. type NamedServiceResolverActivatorFunc[T any] func(types.Resolver) func(string) (T, error) +// CreateNamedServiceResolverActivatorFunc creates a NamedServiceResolverActivatorFunc for resolving named services. func CreateNamedServiceResolverActivatorFunc[T any]() NamedServiceResolverActivatorFunc[T] { return func(resolver types.Resolver) func(string) (T, error) { var nilInstance T diff --git a/pkg/resolving/resolver.go b/pkg/resolving/resolver.go index d2de070..f63338f 100644 --- a/pkg/resolving/resolver.go +++ b/pkg/resolving/resolver.go @@ -14,6 +14,7 @@ type resolver struct { globalInstances *core.InstanceBag } +// ResolveRequiredServices resolves all registered services of a specified type T using the given resolver and context. func ResolveRequiredServices[T any](resolver types.Resolver, ctx context.Context) ([]T, error) { t := reflect.TypeOf((*T)(nil)).Elem() switch t.Kind() { @@ -37,6 +38,8 @@ func ResolveRequiredServices[T any](resolver types.Resolver, ctx context.Context return result, err } +// ResolveRequiredService resolves a single service instance of the specified type using the given resolver and context. +// The method can return the following errors: ErrorCannotResolveService, ErrorAmbiguousServiceInstancesResolved. func ResolveRequiredService[T any](resolver types.Resolver, ctx context.Context) (T, error) { var nilInstance T services, err := ResolveRequiredServices[T](resolver, ctx) @@ -51,6 +54,7 @@ func ResolveRequiredService[T any](resolver types.Resolver, ctx context.Context) return nilInstance, types.NewResolverError(types.ErrorCannotResolveService) } +// NewResolver creates and returns a new Resolver instance based on the provided ServiceRegistry. func NewResolver(registry types.ServiceRegistry) types.Resolver { r := &resolver{ registry: registry, @@ -90,10 +94,12 @@ func (r *resolver) createResolverRegistryAccessor(resolverOptions ...types.Resol return r.registry, nil } +// Resolve returns a list of instances associated with the specified service type. func (r *resolver) Resolve(ctx context.Context, serviceType types.ServiceType) ([]interface{}, error) { return r.ResolveWithOptions(ctx, serviceType) } +// ResolveWithOptions resolves instances for the given service type with the provided resolver options. func (r *resolver) ResolveWithOptions(ctx context.Context, serviceType types.ServiceType, resolverOptions ...types.ResolverOptionsFunc) ([]interface{}, error) { registry, registryErr := r.createResolverRegistryAccessor(resolverOptions...) diff --git a/pkg/resolving/resolver_options.go b/pkg/resolving/resolver_options.go index f5b3459..66d93e5 100644 --- a/pkg/resolving/resolver_options.go +++ b/pkg/resolving/resolver_options.go @@ -15,6 +15,7 @@ func applyResolverOptions(registry types.ServiceRegistry, options ...types.Resol return nil } +// WithInstance Creates a ResolverOptionsFunc that registers a specific instance of a type T with a service registry to be resolved as a singleton. func WithInstance[T any](instance T) types.ResolverOptionsFunc { return func(registry types.ServiceRegistry) error { err := registration.RegisterInstance[T](registry, instance) diff --git a/pkg/resolving/scope.go b/pkg/resolving/scope.go index f970455..14f6483 100644 --- a/pkg/resolving/scope.go +++ b/pkg/resolving/scope.go @@ -5,6 +5,7 @@ import ( "github.com/matzefriedrich/parsley/internal/core" ) +// NewScopedContext creates a new context with an associated service instance map, useful for managing service lifetimes within scope. func NewScopedContext(ctx context.Context) context.Context { instances := make(map[uint64]interface{}) return context.WithValue(ctx, core.ParsleyContext, instances) diff --git a/pkg/types/dependency_error.go b/pkg/types/dependency_error.go index 710bddb..7c56652 100644 --- a/pkg/types/dependency_error.go +++ b/pkg/types/dependency_error.go @@ -7,13 +7,17 @@ const ( ) var ( + // ErrInstanceAlreadySet is returned when there is an attempt to set an instance that is already set. ErrInstanceAlreadySet = errors.New(ErrorInstanceAlreadySet) ) +// DependencyError represents an error that occurs due to a missing or failed dependency. +// This error type encapsulates a ParsleyError. type DependencyError struct { ParsleyError } +// NewDependencyError creates a new DependencyError with the provided message. func NewDependencyError(msg string) error { err := DependencyError{ParsleyError{Msg: msg}} return err diff --git a/pkg/types/parsley_error.go b/pkg/types/parsley_error.go index b265974..96a1fd1 100644 --- a/pkg/types/parsley_error.go +++ b/pkg/types/parsley_error.go @@ -2,25 +2,31 @@ package types import "errors" +// ParsleyError represents an error with an associated message and optional underlying cause. type ParsleyError struct { cause error Msg string } +// Error returns the message associated with the ParsleyError. func (f ParsleyError) Error() string { return f.Msg } +// Unwrap returns the underlying cause of the ParsleyError, allowing for error unwrapping functionality. func (f ParsleyError) Unwrap() error { return f.cause } +// Is compares the current ParsleyError's message with another error's message to determine if they are the same. func (f ParsleyError) Is(err error) bool { return f.Error() == err.Error() } +// ParsleyErrorFunc is a function type that modifies a given error. type ParsleyErrorFunc func(e error) +// WithCause wraps a given error within a ParsleyError. func WithCause(err error) ParsleyErrorFunc { return func(e error) { var funqErr *ParsleyError @@ -31,10 +37,12 @@ func WithCause(err error) ParsleyErrorFunc { } } +// ParsleyErrorWithServiceTypeName defines an interface for setting the service type name on errors. type ParsleyErrorWithServiceTypeName interface { ServiceTypeName(name string) } +// ForServiceType creates a ParsleyErrorFunc that sets the service type name on errors that implement the ParsleyErrorWithServiceTypeName interface. func ForServiceType(serviceType string) ParsleyErrorFunc { return func(e error) { withServiceTypeErr, ok := e.(ParsleyErrorWithServiceTypeName) @@ -44,15 +52,18 @@ func ForServiceType(serviceType string) ParsleyErrorFunc { } } +// ParsleyAggregateError represents an aggregate of multiple errors. type ParsleyAggregateError struct { errors []error Msg string } +// Error returns the message associated with the ParsleyAggregateError. func (f ParsleyAggregateError) Error() string { return f.Msg } +// Is checks if the given error is equivalent to any error within the ParsleyAggregateError. func (f ParsleyAggregateError) Is(err error) bool { if f.Error() == err.Error() { return true @@ -65,6 +76,7 @@ func (f ParsleyAggregateError) Is(err error) bool { return false } +// WithAggregatedCause returns a ParsleyErrorFunc that sets an aggregated error cause with the provided errors. func WithAggregatedCause(errs ...error) ParsleyErrorFunc { return func(e error) { var funqErr *ParsleyError diff --git a/pkg/types/reflection_error.go b/pkg/types/reflection_error.go index 995e7d0..904ca8a 100644 --- a/pkg/types/reflection_error.go +++ b/pkg/types/reflection_error.go @@ -1,11 +1,13 @@ package types -type reflectionError struct { +// ReflectionError represents an error specifically related to reflection operations, extending ParsleyError. +type ReflectionError struct { ParsleyError } +// NewReflectionError creates a new ReflectionError with a specified message and optional initializers. func NewReflectionError(msg string, initializers ...ParsleyErrorFunc) error { - err := &reflectionError{ + err := &ReflectionError{ ParsleyError: ParsleyError{ Msg: msg, }, diff --git a/pkg/types/registry_error.go b/pkg/types/registry_error.go index 351b1b9..02d6322 100644 --- a/pkg/types/registry_error.go +++ b/pkg/types/registry_error.go @@ -11,29 +11,43 @@ const ( ) var ( + + // ErrRequiresFunctionValue indicates that the provided value is not a function. ErrRequiresFunctionValue = errors.New(ErrorRequiresFunctionValue) - ErrCannotRegisterModule = errors.New(ErrorCannotRegisterModule) + + // ErrCannotRegisterModule indicates that the module registration process has failed. + ErrCannotRegisterModule = errors.New(ErrorCannotRegisterModule) + + // ErrTypeAlreadyRegistered indicates that an attempt was made to register a type that is already registered. ErrTypeAlreadyRegistered = errors.New(ErrorTypeAlreadyRegistered) - ErrFailedToRegisterType = errors.New(ErrorFailedToRegisterType) + + // ErrFailedToRegisterType indicates that the attempt to register a type has failed. + ErrFailedToRegisterType = errors.New(ErrorFailedToRegisterType) ) +// RegistryError represents an error that gets returned for failing registry operations. type RegistryError struct { ParsleyError serviceTypeName string } +// _ ensures that RegistryError implements the ParsleyErrorWithServiceTypeName interface. var _ ParsleyErrorWithServiceTypeName = &RegistryError{} +// ServiceTypeName sets the service type name of the RegistryError. func (r *RegistryError) ServiceTypeName(name string) { r.serviceTypeName = name } +// MatchesServiceType checks if the service type name of the RegistryError matches the specified name. func (r *RegistryError) MatchesServiceType(name string) bool { return r.serviceTypeName == name } +// _ ensures that RegistryError implements the ParsleyErrorWithServiceTypeName interface. var _ ParsleyErrorWithServiceTypeName = &RegistryError{} +// NewRegistryError creates a new RegistryError with the given message and initializers to modify the error. func NewRegistryError(msg string, initializers ...ParsleyErrorFunc) error { err := &RegistryError{ ParsleyError: ParsleyError{ diff --git a/pkg/types/resolver_error.go b/pkg/types/resolver_error.go index c43ebc5..a0092b3 100644 --- a/pkg/types/resolver_error.go +++ b/pkg/types/resolver_error.go @@ -17,27 +17,50 @@ const ( ) var ( - ErrServiceTypeNotRegistered = errors.New(ErrorServiceTypeNotRegistered) - ErrActivatorFunctionInvalidReturnType = errors.New(ErrorCannotResolveService) - ErrCannotBuildDependencyGraph = errors.New(ErrorCannotBuildDependencyGraph) - ErrCircularDependencyDetected = errors.New(ErrorCircularDependencyDetected) - ErrInstanceCannotBeNil = errors.New(ErrorInstanceCannotBeNil) - ErrServiceTypeMustBeInterface = errors.New(ErrorServiceTypeMustBeInterface) - ErrCannotRegisterTypeWithResolverOptions = errors.New(ErrorCannotRegisterTypeWithResolverOptions) + + // ErrServiceTypeNotRegistered is returned when attempting to resolve a service type that has not been registered. + ErrServiceTypeNotRegistered = errors.New(ErrorServiceTypeNotRegistered) + + // ErrRequiredServiceNotRegistered is returned when a required service type is not registered. + ErrRequiredServiceNotRegistered = errors.New(ErrorRequiredServiceNotRegistered) + + // ErrActivatorFunctionInvalidReturnType is returned when an activator function has an invalid return type. + ErrActivatorFunctionInvalidReturnType = errors.New(ErrorCannotResolveService) + + // ErrCannotBuildDependencyGraph is returned when the resolver fails to build a dependency graph due to missing dependencies or other issues. + ErrCannotBuildDependencyGraph = errors.New(ErrorCannotBuildDependencyGraph) + + // ErrCircularDependencyDetected is returned when a circular dependency is detected during the resolution process. + ErrCircularDependencyDetected = errors.New(ErrorCircularDependencyDetected) + + // ErrInstanceCannotBeNil is returned when an instance provided is nil, but a non-nil value is required. + ErrInstanceCannotBeNil = errors.New(ErrorInstanceCannotBeNil) + + // ErrServiceTypeMustBeInterface is returned when a service type is not an interface. + ErrServiceTypeMustBeInterface = errors.New(ErrorServiceTypeMustBeInterface) + + // ErrCannotRegisterTypeWithResolverOptions is returned when the resolver failed to register a type via resolver options. + ErrCannotRegisterTypeWithResolverOptions = errors.New(ErrorCannotRegisterTypeWithResolverOptions) + + // ErrCannotCreateInstanceOfUnregisteredType is returned when the resolver fails to instantiate a type that has not been registered. ErrCannotCreateInstanceOfUnregisteredType = errors.New(ErrorCannotCreateInstanceOfUnregisteredType) ) +// ResolverError represents an error that gets returned for failing service resolver operations. type ResolverError struct { ParsleyError serviceTypeName string } +// _ ensures that the ResolverError implements the ParsleyErrorWithServiceTypeName interface. var _ ParsleyErrorWithServiceTypeName = &ResolverError{} +// ServiceTypeName sets the service type name for the ResolverError instance. func (r *ResolverError) ServiceTypeName(name string) { r.serviceTypeName = name } +// NewResolverError creates a new ResolverError with the provided message and applies optional ParsleyErrorFunc initializers. func NewResolverError(msg string, initializers ...ParsleyErrorFunc) error { err := &ResolverError{ ParsleyError: ParsleyError{ diff --git a/pkg/types/service_type.go b/pkg/types/service_type.go index c1b95ef..d85778d 100644 --- a/pkg/types/service_type.go +++ b/pkg/types/service_type.go @@ -13,33 +13,41 @@ type serviceType struct { lookupKey ServiceKey } +// String returns a formatted string representation of the service type, including its name, and package path. func (s serviceType) String() string { return fmt.Sprintf("Name: \"%s\", Package: \"%s\", List: %t)", s.name, s.packagePath, s.list) } var _ ServiceType = &serviceType{} +// LookupKey returns the lookup key associated with the service type. func (s serviceType) LookupKey() ServiceKey { return s.lookupKey } +// ReflectedType returns the reflected type of the service. func (s serviceType) ReflectedType() reflect.Type { return s.reflectedType } +// Name returns the name of the service type. func (s serviceType) Name() string { return s.name } +// PackagePath returns the package path of the service type. func (s serviceType) PackagePath() string { return s.packagePath } +// MakeServiceType creates a ServiceType instance for the specified generic type T. func MakeServiceType[T any]() ServiceType { elem := reflect.TypeOf(new(T)).Elem() return ServiceTypeFrom(elem) } +// ServiceTypeFrom creates a ServiceType from the given reflect.Type. +// Supports pointer, interface, function, slice, and struct types. The function panics, if t is of an unsupported kind is given. func ServiceTypeFrom(t reflect.Type) ServiceType { isList := false elemType := t diff --git a/pkg/types/types.go b/pkg/types/types.go index 24d9181..f4c097a 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -6,6 +6,7 @@ import ( "reflect" ) +// FunctionInfo Stores information about a service activator function. This interface supports the internal infrastructure. type FunctionInfo interface { fmt.Stringer Name() string @@ -19,93 +20,182 @@ type FunctionParameterInfo interface { Type() ServiceType } +// ServiceKey represents a unique key for identifying services in the service registry. type ServiceKey struct { value string } +// String Gets the value of the current ServiceKey instance. func (s ServiceKey) String() string { return s.value } +// NewServiceKey creates a new ServiceKey with the given value. func NewServiceKey(value string) ServiceKey { return ServiceKey{value: value} } +// ServiceType represents a service type. type ServiceType interface { + + // Name returns the name of the service type. Name() string + + // PackagePath returns the package path of the service type. PackagePath() string + + // ReflectedType returns the underlying reflect.Type representation of the service type. ReflectedType() reflect.Type + + // LookupKey retrieves the ServiceKey associated with the service type. LookupKey() ServiceKey } +// ServiceRegistry provides methods to map service types to activator functions. The service registration organizes and stores the metadata required by the service resolver. type ServiceRegistry interface { ServiceRegistryAccessor + + // CreateLinkedRegistry creates and returns a new ServiceRegistry instance linked to the current registry. A linked service registry is an empty service registry. CreateLinkedRegistry() ServiceRegistry + + // CreateScope creates and returns a scoped ServiceRegistry instance which inherits all service registrations from the current ServiceRegistry instance. CreateScope() ServiceRegistry + + // IsRegistered checks if a service of the specified ServiceType is registered in the service registry. IsRegistered(serviceType ServiceType) bool + + // Register registers a service with its activator function and defines its lifetime scope with the service registry. Register(activatorFunc any, scope LifetimeScope) error + + // RegisterModule registers one or more modules, encapsulated as ModuleFunc, with the service registry. A module is a logical unit of service registrations. RegisterModule(modules ...ModuleFunc) error } +// ModuleFunc defines a function used to register services with the given service registry. type ModuleFunc func(registry ServiceRegistry) error +// ServiceRegistryAccessor provides methods to access and retrieve service registrations from the registry. type ServiceRegistryAccessor interface { + + // TryGetServiceRegistrations attempts to retrieve all service registrations for the given service type. + // Returns the service registration list and true if found, otherwise returns false. TryGetServiceRegistrations(serviceType ServiceType) (ServiceRegistrationList, bool) + + // TryGetSingleServiceRegistration attempts to retrieve a single service registration for the given service type. + // Returns the service registration and true if found, otherwise returns false. TryGetSingleServiceRegistration(serviceType ServiceType) (ServiceRegistration, bool) } +// ServiceRegistration represents a service registrations. type ServiceRegistration interface { + + // Id Returns the unique identifier of the service registration. Id() uint64 + + // InvokeActivator calls the activator function with the provided parameters and returns the resulting instance and any error. InvokeActivator(params ...interface{}) (interface{}, error) + + // IsSame checks if the provided ServiceRegistration equals the current ServiceRegistration. IsSame(other ServiceRegistration) bool + + // LifetimeScope returns the LifetimeScope associated with the service registration. LifetimeScope() LifetimeScope + + // RequiredServiceTypes returns a slice of ServiceType, containing all service types required by the service registration. RequiredServiceTypes() []ServiceType + + // ServiceType retrieves the type of the service being registered. ServiceType() ServiceType } +// ServiceRegistrationList provides functionality to manage a list of service registrations. This interface supports internal infrastructure services. type ServiceRegistrationList interface { + + // AddRegistration adds a new service registration to the list. AddRegistration(registration ServiceRegistrationSetup) error + + // Id returns the unique identifier of the service registration list. Id() uint64 + + // Registrations returns a slice of ServiceRegistration, containing all registrations in the list. Registrations() []ServiceRegistration + + // IsEmpty checks if the service registration list contains any registrations. + // It returns true if the list is empty, otherwise false. IsEmpty() bool } +// ServiceRegistrationSetup extends ServiceRegistration and supports internal infrastructure services. type ServiceRegistrationSetup interface { ServiceRegistration + + // SetId sets the unique identifier for the service registration. This method supports internal infrastructure and is not intended to be used by your code. SetId(id uint64) error } +// NamedService is a generic interface defining a service with a name and an activator function. type NamedService[T any] interface { Name() string ActivatorFunc() any } -type RegistrationConfigurationFunc func(r ServiceRegistration) - +// ResolverOptionsFunc represents a function that configures a service registry used by the resolver. type ResolverOptionsFunc func(registry ServiceRegistry) error +// Resolver provides methods to resolve registered services based on types. type Resolver interface { + + // Resolve attempts to resolve all registered services of the specified ServiceType. Resolve(ctx context.Context, serviceType ServiceType) ([]interface{}, error) + + // ResolveWithOptions resolves services of the specified type using additional options and returns a list of resolved services or an error. ResolveWithOptions(ctx context.Context, serviceType ServiceType, options ...ResolverOptionsFunc) ([]interface{}, error) } +// DependencyInfo provides functionality to manage dependency information. type DependencyInfo interface { + // AddRequiredServiceInfo adds a child dependency to the current dependency info. AddRequiredServiceInfo(child DependencyInfo) + + // CreateInstance creates an instance of the service associated with this dependency info. CreateInstance() (interface{}, error) + + // Consumer returns the parent dependency for the current dependency info. Consumer() DependencyInfo + + // HasInstance checks if an instance has already been created for the dependency represented by the current DependencyInfo object. HasInstance() bool + + // Instance retrieves the created instance of the service associated with this dependency info. Instance() interface{} + + // Registration gets the service registration of the current dependency info. Registration() ServiceRegistration + + // RequiredServiceTypes gets the service types required by this dependency info. RequiredServiceTypes() []ServiceType + + // RequiredServices retrieves the instances of services required by this dependency info. RequiredServices() ([]interface{}, error) + + // ServiceTypeName gets the name of the service type associated with this dependency info. ServiceTypeName() string + + // SetInstance sets the instance for the current dependency info. SetInstance(instance interface{}) error } +// LifetimeScope represents the duration for which a service or object instance is retained. type LifetimeScope uint const ( + + // LifetimeTransient represents a transient lifetime where a new instance is created each time it is requested. LifetimeTransient LifetimeScope = iota + + // LifetimeScoped represents a scoped lifetime where a single instance is created per scope. LifetimeScoped + + // LifetimeSingleton represents a single instance scope that persists for the lifetime of the application. LifetimeSingleton )