package web import ( "errors" "io" "net/http" "git.akyoto.dev/go/router" ) // Context represents the interface for a request & response context. type Context interface { Copy(io.Reader) error Bytes([]byte) error Error(...any) error File(string) error Get(string) string Next() error Redirect(int, string) error Request() Request Response() Response Status(int) Context String(string) error } // ctx represents a request & response context. type ctx struct { request request response response server *server params []router.Parameter handlerCount uint8 } // Bytes responds with a raw byte slice. func (ctx *ctx) Bytes(body []byte) error { _, err := ctx.response.Write(body) return err } // Copy sends the contents of the io.Reader without creating an in-memory copy. func (ctx *ctx) Copy(reader io.Reader) error { _, err := io.Copy(ctx.response.ResponseWriter, reader) return err } // Error is used for sending error messages to the client. func (ctx *ctx) Error(messages ...any) error { var combined []error for _, msg := range messages { switch err := msg.(type) { case error: combined = append(combined, err) case string: combined = append(combined, errors.New(err)) } } return errors.Join(combined...) } // Get retrieves a parameter. func (ctx *ctx) Get(param string) string { for i := range len(ctx.params) { p := ctx.params[i] if p.Key == param { return p.Value } } return "" } // File serves the file at the given path. func (ctx *ctx) File(path string) error { http.ServeFile(ctx.response.ResponseWriter, ctx.request.Request, path) return nil } // Next executes the next handler in the middleware chain. func (ctx *ctx) Next() error { ctx.handlerCount++ return ctx.server.handlers[ctx.handlerCount](ctx) } // Request returns the HTTP request. func (ctx *ctx) Request() Request { return &ctx.request } // Response returns the HTTP response. func (ctx *ctx) Response() Response { return &ctx.response } // Redirect sets the Location header and writes the headers with the given status code. func (ctx *ctx) Redirect(code int, url string) error { ctx.Response().SetHeader("Location", url) ctx.Status(code) return nil } // Status sets the HTTP status of the response. func (ctx *ctx) Status(status int) Context { ctx.response.WriteHeader(status) return ctx } // String responds with the given string. func (ctx *ctx) String(body string) error { _, err := ctx.response.WriteString(body) return err } // addParameter adds a new parameter to the context. func (ctx *ctx) addParameter(key string, value string) { ctx.params = append(ctx.params, router.Parameter{ Key: key, Value: value, }) }