Skip to content

Commit

Permalink
feat: adding containers
Browse files Browse the repository at this point in the history
  • Loading branch information
hokamsingh committed Aug 22, 2024
1 parent 8c31388 commit 37ec295
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 5 deletions.
129 changes: 129 additions & 0 deletions internal/core/injector/injector.go
Original file line number Diff line number Diff line change
@@ -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
}
18 changes: 18 additions & 0 deletions internal/core/module/module.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package module

type IModule interface {
GetName() string
GetControllers() []interface{}
GetServices() []interface{}
}

type Module struct {
Name string
Controllers []interface{}
Expand All @@ -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
}
25 changes: 21 additions & 4 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package utils

import (
"crypto/rand"
"encoding/hex"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -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))
Expand All @@ -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
}
7 changes: 6 additions & 1 deletion pkg/lessgo/less.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

0 comments on commit 37ec295

Please sign in to comment.