package router import ( "io" "net/http" "net/url" "somehole.com/common/log" ) type Error interface { Error(e Error) (err Error) Status() (code int) String() (out string) Response() (res *GenericResponse) } type GenericRequest struct { Url url.URL Header Header Values Values Body Body } type Request[RQB RequestBuilder] struct { GenericRequest requestBuilder RQB } func NewRequest[RQB RequestBuilder](rqb RQB) *Request[RQB] { return &Request[RQB]{ GenericRequest: *rqb.Request(), requestBuilder: rqb, } } func (*Request[RQB]) RequestBuilder() RequestBuilder { var rqb RQB return rqb.New() } type RequestBuilder interface { mustEmbedDefaultRequestBuilder() New() (rqb RequestBuilder) SetAllowed(method string) (err Error) SetUrl(url url.URL) (err Error) SetHeader(map[string][]string) (err Error) SetValues(map[string][]string) (err Error) SetBody(body io.ReadCloser) (err Error) Request() *GenericRequest } func NewRequestBuilder[RQB RequestBuilder]() RQB { var rqb RQB return rqb.New().(RQB) } type GenericResponse struct { Status int Header Header Body Body } type Response[RSB ResponseBuilder] struct { GenericResponse responseBuilder RSB } func NewResponse[RSB ResponseBuilder](rsb RSB) *Response[RSB] { return &Response[RSB]{ GenericResponse: *rsb.Response(), responseBuilder: rsb, } } func (res *Response[RSB]) ResponseBuilder() RSB { return res.responseBuilder } type ResponseBuilder interface { mustEmbedDefaultResponseBuilder() New() (rsb ResponseBuilder) SetHeader(map[string][]string) (err Error) SetBody(body []byte) (err Error) Response() *GenericResponse } func NewResponseBuilder[RSB ResponseBuilder]() RSB { var rsb RSB return rsb.New().(RSB) } type writer struct { http.ResponseWriter log.Logger } func (w *writer) handleError(err Error) (ok bool) { if err != nil { res := err.Response() for key, value := range res.Header { for _, v := range value { w.Header().Add(key, v) } } w.WriteHeader(res.Status) w.Write(res.Body) w.Logf(log.LevelError, err.String()) return false } return true } type serveStage uint8 const ( servePre serveStage = iota serveMain servePost numServeStages ) type ServeFunc[RQB RequestBuilder, RSB ResponseBuilder] func(req *Request[RQB], rsb RSB) (err Error) type server[RQB RequestBuilder, RSB ResponseBuilder] struct { logger log.Logger serve [numServeStages][]ServeFunc[RQB, RSB] } func NewServer[RQB RequestBuilder, RSB ResponseBuilder](serve ServeFunc[RQB, RSB]) (srv *server[RQB, RSB]) { srv = &server[RQB, RSB]{ serve: [numServeStages][]ServeFunc[RQB, RSB]{}, } srv.serve[serveMain] = append(srv.serve[serveMain], serve) return srv } func (srv *server[RQB, RSB]) SetLogger(logger log.Logger) *server[RQB, RSB] { srv.logger = logger return srv } func (srv *server[RQB, RSB]) addServeFunc(stage serveStage, serve ServeFunc[RQB, RSB]) *server[RQB, RSB] { if srv.serve[stage] == nil { srv.serve[stage] = make([]ServeFunc[RQB, RSB], 0) } srv.serve[stage] = append(srv.serve[stage], serve) return srv } func (srv *server[RQB, RSB]) PreServe(serve ServeFunc[RQB, RSB]) *server[RQB, RSB] { return srv.addServeFunc(servePre, serve) } func (srv *server[RQB, RSB]) AddServe(serve ServeFunc[RQB, RSB]) *server[RQB, RSB] { return srv.addServeFunc(serveMain, serve) } func (srv *server[RQB, RSB]) PostServe(serve ServeFunc[RQB, RSB]) *server[RQB, RSB] { return srv.addServeFunc(servePost, serve) } func (srv *server[RQB, RSB]) ServeHTTP(w http.ResponseWriter, r *http.Request) { var ( rqb = NewRequestBuilder[RQB]() rsb = NewResponseBuilder[RSB]() ) wr := writer{ResponseWriter: w, Logger: srv.logger} if ok := wr.handleError(rqb.SetAllowed(r.Method)); !ok { return } if ok := wr.handleError(rqb.SetUrl(*r.URL)); !ok { return } if ok := wr.handleError(rqb.SetHeader(r.Header)); !ok { return } if ok := wr.handleError(rqb.SetBody(r.Body)); !ok { return } r.ParseForm() if ok := wr.handleError(rqb.SetValues(r.Form)); !ok { return } req := NewRequest(rqb) for _, stage := range srv.serve { for _, serve := range stage { err := serve(req, rsb) if ok := wr.handleError(err); !ok { return } } } res := NewResponse(rsb) if res.Header != nil { for key, value := range res.Header { for _, v := range value { wr.Header().Add(key, v) } } } wr.WriteHeader(res.Status) if len(res.Body) > 0 { wr.Write(res.Body) } }