refactor: new (simpler) version of the sl library
This commit is contained in:
+10
-12
@@ -8,7 +8,6 @@ import (
|
||||
"phc/website/services/database"
|
||||
"phc/website/services/server"
|
||||
"phc/website/services/server/dev"
|
||||
lista_utenti "phc/website/services/server/lista-utenti"
|
||||
"phc/website/sl"
|
||||
)
|
||||
|
||||
@@ -17,23 +16,20 @@ func main() {
|
||||
|
||||
// sl.Inject[config.Interface](l, &config.EnvConfig{})
|
||||
|
||||
sl.InjectValue[config.Interface](l, &config.Custom{
|
||||
ModeValue: "production",
|
||||
HostValue: ":4000",
|
||||
sl.InjectValue(l, config.Slot, &config.Config{
|
||||
Mode: "production",
|
||||
Host: ":4000",
|
||||
})
|
||||
|
||||
sl.InjectValue[database.Database](l, &database.Memory{})
|
||||
sl.InjectValue(l, database.Slot, database.Database(
|
||||
&database.Memory{}),
|
||||
)
|
||||
|
||||
sl.Inject(l, &dev.Dev{})
|
||||
sl.Inject(l, &lista_utenti.ListaUtenti{})
|
||||
sl.Inject(l, &server.Server{})
|
||||
|
||||
_, err := sl.Use[*server.Server](l)
|
||||
if err != nil {
|
||||
if _, err := server.Configure(l); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dev, err := sl.Use[*dev.Dev](l)
|
||||
dev, err := sl.Use(l, dev.Slot)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -48,4 +44,6 @@ func main() {
|
||||
if err := enc.Encode(dev.HtmlRouteBindings); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf(`generated "out/routes.json"`)
|
||||
}
|
||||
|
||||
+6
-14
@@ -6,22 +6,18 @@ import (
|
||||
"phc/website/services/config"
|
||||
"phc/website/services/database"
|
||||
"phc/website/services/server"
|
||||
"phc/website/services/server/dev"
|
||||
lista_utenti "phc/website/services/server/lista-utenti"
|
||||
"phc/website/sl"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := sl.New()
|
||||
|
||||
// sl.Inject[config.Interface](l, &config.EnvConfig{})
|
||||
|
||||
config := sl.InjectValue[config.Interface](l, &config.Custom{
|
||||
ModeValue: "production",
|
||||
HostValue: ":4000",
|
||||
cfg := sl.InjectValue(l, config.Slot, &config.Config{
|
||||
Mode: "production",
|
||||
Host: ":4000",
|
||||
})
|
||||
|
||||
sl.InjectValue[database.Database](l, &database.Memory{
|
||||
sl.InjectValue[database.Database](l, database.Slot, &database.Memory{
|
||||
Users: []model.User{
|
||||
{
|
||||
Id: "claire",
|
||||
@@ -38,14 +34,10 @@ func main() {
|
||||
},
|
||||
})
|
||||
|
||||
sl.Inject(l, &dev.Dev{})
|
||||
sl.Inject(l, &lista_utenti.ListaUtenti{})
|
||||
sl.Inject(l, &server.Server{})
|
||||
|
||||
server, err := sl.Use[*server.Server](l)
|
||||
srv, err := server.Configure(l)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Fatal(server.Router.Listen(config.Host()))
|
||||
log.Fatal(srv.Router.Listen(cfg.Host))
|
||||
}
|
||||
|
||||
+14
-31
@@ -6,47 +6,30 @@ import (
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
sl.Service
|
||||
var Slot = sl.NewSlot[*Config]()
|
||||
|
||||
Mode() string
|
||||
Host() string
|
||||
type Config struct {
|
||||
Mode string
|
||||
Host string
|
||||
}
|
||||
|
||||
type Custom struct {
|
||||
ModeValue string
|
||||
HostValue string
|
||||
}
|
||||
|
||||
func (c Custom) Mode() string { return c.ModeValue }
|
||||
func (c Custom) Host() string { return c.HostValue }
|
||||
|
||||
func (Custom) Initialize(l *sl.ServiceLocator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnvConfig struct {
|
||||
mode string
|
||||
host string
|
||||
}
|
||||
|
||||
func (c EnvConfig) Mode() string { return c.mode }
|
||||
func (c EnvConfig) Host() string { return c.host }
|
||||
|
||||
func (c *EnvConfig) Initialize(l *sl.ServiceLocator) error {
|
||||
func Load(l *sl.ServiceLocator) (*Config, error) {
|
||||
m, err := godotenv.Read(".env")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.mode = "production"
|
||||
mode := "production"
|
||||
if v, ok := m["MODE"]; ok {
|
||||
c.mode = v
|
||||
mode = v
|
||||
}
|
||||
c.host = ":4000"
|
||||
host := ":4000"
|
||||
if v, ok := m["HOST"]; ok {
|
||||
c.host = v
|
||||
host = v
|
||||
}
|
||||
|
||||
return nil
|
||||
return &Config{
|
||||
mode,
|
||||
host,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"phc/website/sl"
|
||||
)
|
||||
|
||||
type Database interface {
|
||||
sl.Service
|
||||
var Slot = sl.NewSlot[Database]()
|
||||
|
||||
type Database interface {
|
||||
CreateUser(user model.User) error
|
||||
ReadUser(id string) (model.User, error)
|
||||
ReadUsers() ([]model.User, error)
|
||||
@@ -20,8 +20,6 @@ type Memory struct {
|
||||
Users []model.User
|
||||
}
|
||||
|
||||
func (m *Memory) Initialize(l *sl.ServiceLocator) error { return nil }
|
||||
|
||||
func (m *Memory) CreateUser(user model.User) error {
|
||||
m.Users = append(m.Users, user)
|
||||
return nil
|
||||
|
||||
+16
-12
@@ -9,35 +9,39 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var Slot = sl.NewSlot[*Dev]()
|
||||
|
||||
type Dev struct {
|
||||
HtmlRouteBindings map[string]string
|
||||
}
|
||||
|
||||
func (m *Dev) Initialize(l *sl.ServiceLocator) error {
|
||||
m.HtmlRouteBindings = map[string]string{}
|
||||
|
||||
router, err := sl.Use[*routes.Router](l)
|
||||
if err != nil {
|
||||
return err
|
||||
func New(l *sl.ServiceLocator) (*Dev, error) {
|
||||
dev := &Dev{
|
||||
map[string]string{},
|
||||
}
|
||||
|
||||
router.Get("/api/dev/routes", func(c *fiber.Ctx) error {
|
||||
return c.JSON(m.HtmlRouteBindings)
|
||||
router, err := sl.Use(l, routes.Api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
router.Get("/dev/routes", func(c *fiber.Ctx) error {
|
||||
return c.JSON(dev.HtmlRouteBindings)
|
||||
})
|
||||
|
||||
return nil
|
||||
return dev, nil
|
||||
}
|
||||
|
||||
// UseVitePage this hook will link the provided "mountPoint" to the "frontendHtml" page
|
||||
func UseVitePage[T any](l *sl.ServiceLocator, mountPoint, frontendHtml string) func(c *fiber.Ctx) error {
|
||||
func UseVitePage(l *sl.ServiceLocator, mountPoint, frontendHtml string) func(c *fiber.Ctx) error {
|
||||
log.Printf(`registering vite route %q for %q`, frontendHtml, mountPoint)
|
||||
|
||||
dev, err := sl.Use[*Dev](l)
|
||||
dev, err := sl.Use(l, Slot)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dev.HtmlRouteBindings[mountPoint] = path.Join("./frontend/", frontendHtml)
|
||||
dev.HtmlRouteBindings[mountPoint] = frontendHtml
|
||||
|
||||
return func(c *fiber.Ctx) error {
|
||||
return c.SendFile(path.Join("./out/frontend/", frontendHtml))
|
||||
|
||||
@@ -9,24 +9,22 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type ListaUtenti struct{}
|
||||
|
||||
func (s *ListaUtenti) Initialize(l *sl.ServiceLocator) error {
|
||||
|
||||
db, err := sl.Use[database.Database](l)
|
||||
func Configure(l *sl.ServiceLocator) error {
|
||||
db, err := sl.Use(l, database.Slot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
router, err := sl.Use[*routes.Router](l)
|
||||
r, err := sl.Use(l, routes.Root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
router.Get("/utenti",
|
||||
dev.UseVitePage[ListaUtenti](l, "/utenti", "pages/lista-utenti/index.html"),
|
||||
r.Get("/utenti",
|
||||
dev.UseVitePage(l, "/utenti", "pages/lista-utenti/index.html"),
|
||||
)
|
||||
router.Get("/api/lista-utenti", func(c *fiber.Ctx) error {
|
||||
|
||||
r.Get("/api/lista-utenti", func(c *fiber.Ctx) error {
|
||||
users, err := db.ReadUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"net/http"
|
||||
"phc/website/model"
|
||||
"phc/website/services/database"
|
||||
"phc/website/services/server/dev"
|
||||
lista_utenti "phc/website/services/server/lista-utenti"
|
||||
"phc/website/services/server/routes"
|
||||
"phc/website/sl"
|
||||
"testing"
|
||||
@@ -22,7 +20,7 @@ import (
|
||||
func Test1(t *testing.T) {
|
||||
l := sl.New()
|
||||
|
||||
sl.InjectValue[database.Database](l, &database.Memory{
|
||||
sl.InjectValue[database.Database](l, database.Slot, &database.Memory{
|
||||
Users: []model.User{
|
||||
{
|
||||
Id: "claire",
|
||||
@@ -39,11 +37,8 @@ func Test1(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
sl.Inject(l, &dev.Dev{})
|
||||
sl.Inject(l, &lista_utenti.ListaUtenti{})
|
||||
|
||||
r := fiber.New()
|
||||
sl.InjectValue(l, routes.NewRouter(r))
|
||||
sl.InjectValue(l, routes.Root, fiber.Router(r))
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:4000/api/lista-utenti", nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,10 +6,6 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func NewRouter(r fiber.Router) *Router {
|
||||
return &Router{r}
|
||||
}
|
||||
var Root = sl.NewSlot[fiber.Router]()
|
||||
|
||||
type Router struct{ fiber.Router }
|
||||
|
||||
func (s *Router) Initialize(l *sl.ServiceLocator) error { return nil }
|
||||
var Api = sl.NewSlot[fiber.Router]()
|
||||
|
||||
@@ -11,16 +11,21 @@ import (
|
||||
|
||||
type Server struct{ Router *fiber.App }
|
||||
|
||||
func (s *Server) Initialize(l *sl.ServiceLocator) error {
|
||||
s.Router = fiber.New(fiber.Config{})
|
||||
sl.InjectValue(l, routes.NewRouter(s.Router))
|
||||
func Configure(l *sl.ServiceLocator) (*Server, error) {
|
||||
r := fiber.New(fiber.Config{})
|
||||
|
||||
if _, err := sl.Use[*dev.Dev](l); err != nil {
|
||||
return err
|
||||
sl.InjectValue(l, routes.Root, r.Group("/"))
|
||||
sl.InjectValue(l, routes.Api, r.Group("/api"))
|
||||
|
||||
devServerInterop, err := dev.New(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := sl.Use[*lista_utenti.ListaUtenti](l); err != nil {
|
||||
return err
|
||||
sl.InjectValue(l, dev.Slot, devServerInterop)
|
||||
|
||||
if err := lista_utenti.Configure(l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return &Server{r}, nil
|
||||
}
|
||||
|
||||
@@ -5,69 +5,146 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
initialized bool
|
||||
service Service
|
||||
type SlotKey[T any] *struct{}
|
||||
|
||||
func NewSlot[T any]() SlotKey[T] {
|
||||
return SlotKey[T](new(struct{}))
|
||||
}
|
||||
|
||||
type slot struct {
|
||||
createFunc func(*ServiceLocator) (any, error)
|
||||
created bool
|
||||
value any
|
||||
|
||||
typ string
|
||||
}
|
||||
|
||||
func (s *slot) checkInitialized(l *ServiceLocator) error {
|
||||
if !s.created {
|
||||
v, err := s.createFunc(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf(`initialized lazy value of type %T for slot of type %s`, v, s.typ)
|
||||
|
||||
s.value = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServiceLocator struct {
|
||||
providers map[string]*provider
|
||||
hooks map[string][]Service
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Initialize(l *ServiceLocator) error
|
||||
providers map[any]*slot
|
||||
}
|
||||
|
||||
func New() *ServiceLocator {
|
||||
return &ServiceLocator{
|
||||
providers: map[string]*provider{},
|
||||
hooks: map[string][]Service{},
|
||||
providers: map[any]*slot{},
|
||||
}
|
||||
}
|
||||
|
||||
func getTypeName[T any]() string {
|
||||
var v T
|
||||
return fmt.Sprintf(`%T`, &v)[1:]
|
||||
}
|
||||
func InjectValue[T any](l *ServiceLocator, slotKey SlotKey[T], value T) T {
|
||||
log.Printf(`injected value of type %T for slot of type %s`, value, getTypeName[T]())
|
||||
|
||||
// Inject will set the implementation for "S" to "value" (the service will be initialized when needed after all of its dependencies)
|
||||
func Inject[S Service](l *ServiceLocator, value S) {
|
||||
key := getTypeName[S]()
|
||||
log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
l.providers[key] = &provider{false, value}
|
||||
}
|
||||
|
||||
// InjectValue will set the implementation for "S" to "value" and mark this service as already initialized (as this is just a constant)
|
||||
func InjectValue[S Service](l *ServiceLocator, value S) S {
|
||||
key := getTypeName[S]()
|
||||
log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
l.providers[key] = &provider{true, value}
|
||||
l.providers[slotKey] = &slot{nil, true, value, getTypeName[T]()}
|
||||
return value
|
||||
}
|
||||
|
||||
// Use will retrive from the service locator the implementation set for the type "T" and initialize the service if yet to be initialized
|
||||
func Use[T Service](l *ServiceLocator) (T, error) {
|
||||
provider, ok := l.providers[getTypeName[T]()]
|
||||
func InjectLazy[T any](l *ServiceLocator, slotKey SlotKey[T], createFunc func(*ServiceLocator) (T, error)) {
|
||||
log.Printf(`injected lazy for slot of type %s`, getTypeName[T]())
|
||||
|
||||
l.providers[slotKey] = &slot{
|
||||
createFunc: func(l *ServiceLocator) (any, error) {
|
||||
return createFunc(l)
|
||||
},
|
||||
created: false,
|
||||
value: nil,
|
||||
typ: getTypeName[T](),
|
||||
}
|
||||
}
|
||||
|
||||
func Use[T any](l *ServiceLocator, slotKey SlotKey[T]) (T, error) {
|
||||
var zero T
|
||||
|
||||
slot, ok := l.providers[slotKey]
|
||||
if !ok {
|
||||
var zero T
|
||||
return zero, fmt.Errorf(`no injected value for type "%T"`, zero)
|
||||
return zero, fmt.Errorf(`no injected value for type %s`, getTypeName[T]())
|
||||
}
|
||||
|
||||
if provider.initialized {
|
||||
service := provider.service.(T)
|
||||
return service, nil
|
||||
}
|
||||
|
||||
log.Printf(`initializing %T`, provider.service)
|
||||
if err := provider.service.Initialize(l); err != nil {
|
||||
var zero T
|
||||
err := slot.checkInitialized(l)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
provider.initialized = true
|
||||
service := provider.service.(T)
|
||||
return service, nil
|
||||
v := slot.value.(T)
|
||||
|
||||
log.Printf(`using slot of type %s with value of type %T`, getTypeName[T](), v)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// // Require forces the initialization of a slot
|
||||
// func Require[T any](l *ServiceLocator, slotKey SlotKey[T]) error {
|
||||
// var zero T
|
||||
|
||||
// slot, ok := l.providers[slotKey]
|
||||
// if !ok {
|
||||
// return fmt.Errorf(`no injected value for type %T`, zero)
|
||||
// }
|
||||
|
||||
// return slot.checkInitialized(l)
|
||||
// }
|
||||
|
||||
// // MustRequire forces the initialization of a slot or panics if the slot is missing
|
||||
// func MustRequire[T any](l *ServiceLocator, slotKey SlotKey[T]) {
|
||||
// err := Require(l, slotKey)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Inject will set the implementation for "S" to "value" (the service will be initialized when needed after all of its dependencies)
|
||||
// func Inject[S Service](l *Context, value S) {
|
||||
// key := getTypeName[S]()
|
||||
// log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
// l.providers[key] = &provider{false, nil, value}
|
||||
// }
|
||||
|
||||
// // InjectValue will set the implementation for "S" to "value" and mark this service as already initialized (as this is just a constant)
|
||||
// func InjectValue[S Service](l *Context, value S) S {
|
||||
// key := getTypeName[S]()
|
||||
// log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
// l.providers[key] = &provider{true, nil, value}
|
||||
// return value
|
||||
// }
|
||||
|
||||
// // Use will retrive from the service locator the implementation set for the type "T" and initialize the service if yet to be initialized
|
||||
// func Use[T Service](l *Context) (T, error) {
|
||||
// provider, ok := l.providers[getTypeName[T]()]
|
||||
// if !ok {
|
||||
// var zero T
|
||||
// return zero, fmt.Errorf(`no injected value for type "%T"`, zero)
|
||||
// }
|
||||
|
||||
// if provider.initialized {
|
||||
// service := provider.service.(T)
|
||||
// return service, nil
|
||||
// }
|
||||
|
||||
// log.Printf(`initializing %T`, provider.service)
|
||||
// if err := provider.service.Initialize(l); err != nil {
|
||||
// var zero T
|
||||
// return zero, err
|
||||
// }
|
||||
|
||||
// provider.initialized = true
|
||||
// service := provider.service.(T)
|
||||
// return service, nil
|
||||
// }
|
||||
|
||||
func getTypeName[T any]() string {
|
||||
var zero T
|
||||
return fmt.Sprintf(`%T`, &zero)[1:]
|
||||
}
|
||||
|
||||
+2
-3
@@ -17,7 +17,6 @@ const retriveGoRoutes = {
|
||||
|
||||
const routesReq = await fetch('http://127.0.0.1:4000/api/dev/routes')
|
||||
const routes = await routesReq.json()
|
||||
console.dir(routes, { depth: null })
|
||||
|
||||
return routes
|
||||
},
|
||||
@@ -51,8 +50,8 @@ export default defineConfig(async config => {
|
||||
configureServer(server) {
|
||||
Object.entries(routes).forEach(([route, file]) => {
|
||||
server.middlewares.use(route, async (req, res, next) => {
|
||||
let htmlPage = await readFile(resolve(__dirname, file), 'utf8')
|
||||
htmlPage = htmlPage.replace(/\.\//g, dirname(file) + '/')
|
||||
let htmlPage = await readFile(resolve(__dirname, './frontend/', file), 'utf8')
|
||||
htmlPage = htmlPage.replace(/\.\//g, '/' + dirname(file) + '/')
|
||||
|
||||
const url = file
|
||||
const html = await server.transformIndexHtml(url, htmlPage)
|
||||
|
||||
Reference in New Issue
Block a user