diff --git a/internal/core/injector/injector.go b/internal/core/injector/injector.go new file mode 100644 index 0000000..a781982 --- /dev/null +++ b/internal/core/injector/injector.go @@ -0,0 +1,129 @@ +package injector + +import ( + "errors" + "reflect" + "sync" +) + +type Scope int + +const ( + Singleton Scope = iota + Transient + Scoped +) + +type Provider struct { + Factory reflect.Value + Scope Scope +} + +type Container struct { + mu sync.RWMutex + providers map[string]Provider + instances map[string]reflect.Value + scoped map[string]map[string]reflect.Value // Scoped instances are tracked per provider name +} + +func NewContainer() *Container { + return &Container{ + providers: make(map[string]Provider), + instances: make(map[string]reflect.Value), + scoped: make(map[string]map[string]reflect.Value), + } +} + +// Register registers a provider in the container with a specific name and scope. +func (c *Container) Register(name string, provider interface{}, scope Scope) error { + c.mu.Lock() + defer c.mu.Unlock() + + if _, exists := c.providers[name]; exists { + return errors.New("provider already registered") + } + + c.providers[name] = Provider{ + Factory: reflect.ValueOf(provider), + Scope: scope, + } + + // Initialize scoped map for the provider + if scope == Scoped { + c.scoped[name] = make(map[string]reflect.Value) + } + + return nil +} + +// Resolve resolves an instance by its name and handles different scopes. +func (c *Container) Resolve(name string, scopeID ...string) (interface{}, error) { + c.mu.RLock() + defer c.mu.RUnlock() + + provider, exists := c.providers[name] + if !exists { + return nil, errors.New("provider not found") + } + + // Handle singleton scope + if provider.Scope == Singleton { + if instance, exists := c.instances[name]; exists { + return instance.Interface(), nil + } + } + + // Handle scoped scope + if provider.Scope == Scoped { + if len(scopeID) == 0 { + return nil, errors.New("scope ID is required for scoped providers") + } + if instance, exists := c.scoped[name][scopeID[0]]; exists { + return instance.Interface(), nil + } + } + + // Create new instance + result := provider.Factory.Call([]reflect.Value{}) + if len(result) != 1 { + return nil, errors.New("provider must return exactly one value") + } + instance := result[0] + + // Store the instance according to the scope + if provider.Scope == Singleton { + c.instances[name] = instance + } else if provider.Scope == Scoped { + c.scoped[name][scopeID[0]] = instance + } + + return instance.Interface(), nil +} + +// Cleanup cleans up all instances in the container. +func (c *Container) Cleanup() { + c.mu.Lock() + defer c.mu.Unlock() + + for name, instance := range c.instances { + if lifecycle, ok := instance.Interface().(Lifecycle); ok { + _ = lifecycle.Cleanup() + } + delete(c.instances, name) + } + + for name, scopedInstances := range c.scoped { + for scopeID, instance := range scopedInstances { + if lifecycle, ok := instance.Interface().(Lifecycle); ok { + _ = lifecycle.Cleanup() + } + delete(c.scoped[name], scopeID) + } + } +} + +// Lifecycle interface provides hooks for initialization and cleanup. +type Lifecycle interface { + Init() error + Cleanup() error +} diff --git a/internal/core/module/module.go b/internal/core/module/module.go index f34804b..f87eb3e 100644 --- a/internal/core/module/module.go +++ b/internal/core/module/module.go @@ -1,5 +1,11 @@ package module +type IModule interface { + GetName() string + GetControllers() []interface{} + GetServices() []interface{} +} + type Module struct { Name string Controllers []interface{} @@ -13,3 +19,15 @@ func NewModule(name string, controllers []interface{}, services []interface{}) * Services: services, } } + +func (m *Module) GetName() string { + return m.Name +} + +func (m *Module) GetControllers() []interface{} { + return m.Controllers +} + +func (m *Module) GetServices() []interface{} { + return m.Services +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index f63e6b9..470e781 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,6 +1,8 @@ package utils import ( + "crypto/rand" + "encoding/hex" "fmt" "log" "os" @@ -33,8 +35,8 @@ func GetFolderPath(folderName string) (string, error) { // RegisterModuleRoutes is a helper function to register routes for a module. // It will panic if there is an error during registration or if a controller does not implement the required interface. func RegisterModuleRoutes(container *di.Container, r *router.Router, _ interface{}) { - err := container.Invoke(func(module *module.Module) { - for _, ctrl := range module.Controllers { + err := container.Invoke(func(module module.IModule) { + for _, ctrl := range module.GetControllers() { c, ok := ctrl.(controller.Controller) if !ok { panic(fmt.Sprintf("Controller %T does not implement controller.Controller interface", ctrl)) @@ -48,10 +50,25 @@ func RegisterModuleRoutes(container *di.Container, r *router.Router, _ interface } // RegisterModules iterates over a slice of modules and registers their routes. -func RegisterModules(r *router.Router, container *di.Container, modules []module.Module) error { +func RegisterModules(r *router.Router, container *di.Container, modules []module.IModule) error { for _, module := range modules { RegisterModuleRoutes(container, r, module) - log.Print("LessGo :: Registered module ", module.Name) + log.Print("LessGo :: Registered module ", module.GetName()) } return nil } + +// GenerateRandomToken generates a random unique token of the specified length in bytes +func GenerateRandomToken(length int) (string, error) { + // Create a byte slice to hold the random data + token := make([]byte, length) + + // Fill the byte slice with random data + _, err := rand.Read(token) + if err != nil { + return "", fmt.Errorf("failed to generate random token: %v", err) + } + + // Convert the random bytes to a hexadecimal string + return hex.EncodeToString(token), nil +} diff --git a/pkg/lessgo/less.go b/pkg/lessgo/less.go index 40cbc1c..6b037e9 100644 --- a/pkg/lessgo/less.go +++ b/pkg/lessgo/less.go @@ -21,6 +21,7 @@ type Container = di.Container type Middleware = middleware.Middleware type BaseMiddleware = middleware.BaseMiddleware type Module = module.Module +type IModule = module.IModule type Router = router.Router type BaseService = service.BaseService type Service = service.Service @@ -97,6 +98,10 @@ func RegisterModuleRoutes(r *router.Router, container *di.Container, module modu } // RegisterModules iterates over a slice of modules and registers their routes. -func RegisterModules(r *router.Router, container *di.Container, modules []module.Module) error { +func RegisterModules(r *router.Router, container *di.Container, modules []IModule) error { return utils.RegisterModules(r, container, modules) } + +func GenerateRandomToken(len int) (string, error) { + return utils.GenerateRandomToken(len) +}