fix: updated project name and sl documentation

next
Antonio De Lucreziis 2 years ago
parent d185e81ab2
commit 7100009ab1

@ -4,11 +4,12 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"os" "os"
"phc/website/services/config"
"phc/website/services/database" "git.phc.dm.unipi.it/phc/website/services/config"
"phc/website/services/server" "git.phc.dm.unipi.it/phc/website/services/database"
"phc/website/services/server/dev" "git.phc.dm.unipi.it/phc/website/services/server"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/services/server/dev"
"git.phc.dm.unipi.it/phc/website/sl"
) )
func main() { func main() {

@ -5,11 +5,12 @@ import (
"io" "io"
"log" "log"
"os/exec" "os/exec"
"phc/website/model"
"phc/website/services/config" "git.phc.dm.unipi.it/phc/website/model"
"phc/website/services/database" "git.phc.dm.unipi.it/phc/website/services/config"
"phc/website/services/server" "git.phc.dm.unipi.it/phc/website/services/database"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/services/server"
"git.phc.dm.unipi.it/phc/website/sl"
) )
func main() { func main() {

@ -2,11 +2,12 @@ package main
import ( import (
"log" "log"
"phc/website/model"
"phc/website/services/config" "git.phc.dm.unipi.it/phc/website/model"
"phc/website/services/database" "git.phc.dm.unipi.it/phc/website/services/config"
"phc/website/services/server" "git.phc.dm.unipi.it/phc/website/services/database"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/services/server"
"git.phc.dm.unipi.it/phc/website/sl"
) )
func main() { func main() {

@ -1,15 +1,15 @@
module phc/website module git.phc.dm.unipi.it/phc/website
go 1.19 go 1.19
require ( require (
github.com/alecthomas/repr v0.2.0
github.com/gofiber/fiber/v2 v2.41.0 github.com/gofiber/fiber/v2 v2.41.0
github.com/joho/godotenv v1.4.0 github.com/joho/godotenv v1.4.0
github.com/valyala/fasthttp v1.43.0 github.com/valyala/fasthttp v1.43.0
) )
require ( require (
github.com/alecthomas/repr v0.2.0 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect github.com/andybalholm/brotli v1.0.4 // indirect
github.com/klauspost/compress v1.15.9 // indirect github.com/klauspost/compress v1.15.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect

@ -1,7 +1,7 @@
package config package config
import ( import (
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/sl"
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )

@ -2,8 +2,9 @@ package database
import ( import (
"fmt" "fmt"
"phc/website/model"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/sl"
) )
var Slot = sl.NewSlot[Database]() var Slot = sl.NewSlot[Database]()

@ -2,9 +2,10 @@ package articles
import ( import (
"html/template" "html/template"
"phc/website/services/server/dev"
"phc/website/services/server/router" "git.phc.dm.unipi.it/phc/website/services/server/dev"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/services/server/router"
"git.phc.dm.unipi.it/phc/website/sl"
) )
func Configure(l *sl.ServiceLocator) error { func Configure(l *sl.ServiceLocator) error {

@ -6,9 +6,10 @@ import (
"io" "io"
"log" "log"
"path" "path"
"phc/website/services/config"
"phc/website/services/server/routes" "git.phc.dm.unipi.it/phc/website/services/config"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/services/server/routes"
"git.phc.dm.unipi.it/phc/website/sl"
"github.com/alecthomas/repr" "github.com/alecthomas/repr"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"

@ -1,10 +1,10 @@
package listaUtenti package listaUtenti
import ( import (
"phc/website/services/database" "git.phc.dm.unipi.it/phc/website/services/database"
"phc/website/services/server/router" "git.phc.dm.unipi.it/phc/website/services/server/router"
"phc/website/services/server/routes" "git.phc.dm.unipi.it/phc/website/services/server/routes"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/sl"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )

@ -6,12 +6,13 @@ import (
"io" "io"
"net" "net"
"net/http" "net/http"
"phc/website/model"
"phc/website/services/database"
"phc/website/services/server/routes"
"phc/website/sl"
"testing" "testing"
"git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/services/database"
"git.phc.dm.unipi.it/phc/website/services/server/routes"
"git.phc.dm.unipi.it/phc/website/sl"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttputil" "github.com/valyala/fasthttp/fasthttputil"

@ -4,9 +4,10 @@ import (
"bytes" "bytes"
"log" "log"
"os" "os"
"phc/website/services/server/dev"
"phc/website/services/server/routes" "git.phc.dm.unipi.it/phc/website/services/server/dev"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/services/server/routes"
"git.phc.dm.unipi.it/phc/website/sl"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )

@ -1,7 +1,7 @@
package routes package routes
import ( import (
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/sl"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )

@ -1,11 +1,11 @@
package server package server
import ( import (
"phc/website/services/server/articles" "git.phc.dm.unipi.it/phc/website/services/server/articles"
"phc/website/services/server/dev" "git.phc.dm.unipi.it/phc/website/services/server/dev"
"phc/website/services/server/listaUtenti" "git.phc.dm.unipi.it/phc/website/services/server/listaUtenti"
"phc/website/services/server/routes" "git.phc.dm.unipi.it/phc/website/services/server/routes"
"phc/website/sl" "git.phc.dm.unipi.it/phc/website/sl"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )

@ -1,32 +1,46 @@
// The [sl] package has two main concepts, the [ServiceLocator] itself is the main object that one should pass around through the application. A [ServiceLocator] has a list of slots that can be filled with [InjectLazy] and [InjectValue] and retrieved with [Use]. As slots should be unique they can only be created with the [NewSlot] function.
//
// The usual way to use this module is to make slots for go interfaces and then pass implementations using the [InjectValue] and [InjectLazy] functions.
package sl package sl
import ( import (
"fmt" "fmt"
"log" "log"
"os"
) )
type SlotKey[T any] *struct{} // Logger is the debug logger, in the future this will be disabled and discard by default.
//
// As this is the service locator module it was meaning less to pass this through the ServiceLocator itself (without making the whole module more complex)
var Logger *log.Logger = log.New(os.Stderr, "[sl]", log.LstdFlags)
func NewSlot[T any]() SlotKey[T] { // slot is just a "typed" unique "symbol".
return SlotKey[T](new(struct{})) type slot[T any] *struct{}
// NewSlot is the only way to create instances of the slot type. Each instance is unique.
//
// This then lets you attach a service instance of type "T" to a [ServiceLocator] object.
func NewSlot[T any]() slot[T] {
return slot[T](new(struct{}))
} }
type slot struct { // slotEntry represents a service that can lazily initialized (using "createFunc"). Once initialized the instance is kept in the "value" field. The field "typeName" just for debugging purposes.
type slotEntry struct {
createFunc func(*ServiceLocator) (any, error) createFunc func(*ServiceLocator) (any, error)
created bool created bool
value any value any
typ string typeName string
} }
func (s *slot) checkInitialized(l *ServiceLocator) error { func (s *slotEntry) checkInitialized(l *ServiceLocator) error {
if !s.created { if !s.created {
v, err := s.createFunc(l) v, err := s.createFunc(l)
if err != nil { if err != nil {
return err return err
} }
log.Printf(`initialized lazy value of type %T for slot of type %s`, v, s.typ) Logger.Printf(`initialized lazy value of type %T for slot of type %s`, v, s.typeName)
s.created = true s.created = true
s.value = v s.value = v
@ -35,20 +49,25 @@ func (s *slot) checkInitialized(l *ServiceLocator) error {
return nil return nil
} }
// ServiceLocator is the main context passed around to retrive service instances, the interface uses generics so to inject and retrive service instances you should use the functions [InjectValue], [InjectLazy] and [Use].
type ServiceLocator struct { type ServiceLocator struct {
providers map[any]*slot providers map[any]*slotEntry
} }
// New creates a new [ServiceLocator] context to pass around in the application.
func New() *ServiceLocator { func New() *ServiceLocator {
return &ServiceLocator{ return &ServiceLocator{
providers: map[any]*slot{}, providers: map[any]*slotEntry{},
} }
} }
func InjectValue[T any](l *ServiceLocator, slotKey SlotKey[T], value T) T { // InjectValue will inject a concrete instance inside the ServiceLocator "l" for the given "slotKey". This should be used for injecting "static" services, for instances whose construction depend on other services you should use the [InjectLazy] function.
log.Printf(`injected value of type %T for slot of type %s`, value, getTypeName[T]()) //
// This is generic over "T" to check that instances for the given slot type check as "T" can also be an interface.
func InjectValue[T any](l *ServiceLocator, slotKey slot[T], value T) T {
Logger.Printf(`injected value of type %T for slot of type %s`, value, getTypeName[T]())
l.providers[slotKey] = &slot{ l.providers[slotKey] = &slotEntry{
nil, nil,
true, true,
value, value,
@ -57,20 +76,26 @@ func InjectValue[T any](l *ServiceLocator, slotKey SlotKey[T], value T) T {
return value return value
} }
func InjectLazy[T any](l *ServiceLocator, slotKey SlotKey[T], createFunc func(*ServiceLocator) (T, error)) { // InjectLazy will inject an instance inside the given ServiceLocator and "slotKey" that is created only when requested with a call to the [Use] function.
log.Printf(`injected lazy for slot of type %s`, getTypeName[T]()) //
// This is generic over "T" to check that instances for the given slot type check as "T" can also be an interface.
func InjectLazy[T any](l *ServiceLocator, slotKey slot[T], createFunc func(*ServiceLocator) (T, error)) {
Logger.Printf(`injected lazy for slot of type %s`, getTypeName[T]())
l.providers[slotKey] = &slot{ l.providers[slotKey] = &slotEntry{
createFunc: func(l *ServiceLocator) (any, error) { createFunc: func(l *ServiceLocator) (any, error) {
return createFunc(l) return createFunc(l)
}, },
created: false, created: false,
value: nil, value: nil,
typ: getTypeName[T](), typeName: getTypeName[T](),
} }
} }
func Use[T any](l *ServiceLocator, slotKey SlotKey[T]) (T, error) { // Use retrieves the value of type T associated with the given slot key from the provided ServiceLocator instance.
//
// If the ServiceLocator does not have a value for the slot key, or if the value wasn't correctly initialized (in the case of a lazy slot), an error is returned.
func Use[T any](l *ServiceLocator, slotKey slot[T]) (T, error) {
var zero T var zero T
slot, ok := l.providers[slotKey] slot, ok := l.providers[slotKey]
@ -85,71 +110,11 @@ func Use[T any](l *ServiceLocator, slotKey SlotKey[T]) (T, error) {
v := slot.value.(T) v := slot.value.(T)
log.Printf(`using slot of type %s with value of type %T`, getTypeName[T](), v) Logger.Printf(`using slot of type %s with value of type %T`, getTypeName[T](), v)
return v, nil return v, nil
} }
// // Require forces the initialization of a slot // getTypeName is a trick to get the name of a type (even if it is an interface type)
// 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 { func getTypeName[T any]() string {
var zero T var zero T
return fmt.Sprintf(`%T`, &zero)[1:] return fmt.Sprintf(`%T`, &zero)[1:]

Loading…
Cancel
Save