aicra/server.go

101 lines
2.1 KiB
Go

package aicra
import (
"fmt"
"net/http"
"os"
"git.xdrm.io/go/aicra/datatype"
"git.xdrm.io/go/aicra/dynfunc"
"git.xdrm.io/go/aicra/internal/config"
)
// Server represents an AICRA instance featuring: type checkers, services
type Server struct {
config *config.Server
handlers []*apiHandler
}
type apiHandler struct {
Method string
Path string
dynHandler *dynfunc.Handler
}
// New creates a framework instance from a configuration file
func New(configPath string, dtypes ...datatype.T) (*Server, error) {
var (
err error
configFile io.ReadCloser
)
// 1. init instance
var i = &Server{
config: nil,
handlers: make([]*handler, 0),
}
// 2. open config file
configFile, err = os.Open(configPath)
if err != nil {
return nil, err
}
defer configFile.Close()
// 3. load configuration
i.config, err = config.Parse(configFile, dtypes...)
if err != nil {
return nil, err
}
return i, nil
}
// Handle sets a new handler for an HTTP method to a path
func (s *Server) Handle(method, path string, fn interface{}) error {
// find associated service
var service *config.Service
for _, s := range s.config.Services {
if method == s.Method && path == s.Pattern {
service = s
break
}
}
if service == nil {
return fmt.Errorf("%s '%s': %w", method, path, ErrUnknownService)
}
dynHandler, err := dynfunc.Build(fn, *service)
if err != nil {
return fmt.Errorf("%s '%s' handler: %w", method, path, err)
}
s.handlers = append(s.handlers, &apiHandler{
Path: path,
Method: method,
dynHandler: dynHandler,
})
return nil
}
// ToHTTPServer converts the server to a http.Handler
func (s Server) ToHTTPServer() (http.Handler, error) {
for _, service := range s.config.Services {
var hasAssociatedHandler bool
for _, handler := range s.handlers {
if handler.Method == service.Method && handler.Path == service.Pattern {
hasAssociatedHandler = true
break
}
}
if !hasAssociatedHandler {
return nil, fmt.Errorf("%s '%s': %w", service.Method, service.Pattern, ErrMissingHandler)
}
}
return httpHandler(s), nil
}