oweb.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package oweb
  2. import (
  3. "cmp"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "log"
  9. "net/http"
  10. "bllamo.com/client/ollama"
  11. )
  12. func Missing(field string) error {
  13. return &ollama.Error{
  14. Status: 400,
  15. Code: "missing",
  16. Field: field,
  17. Message: fmt.Sprintf("%s is required", field),
  18. }
  19. }
  20. func Invalid(field, value, format string, args ...any) error {
  21. return &ollama.Error{
  22. Status: 400,
  23. Code: "invalid",
  24. Field: field,
  25. Value: value,
  26. Message: fmt.Sprintf(format, args...),
  27. }
  28. }
  29. // Convenience errors
  30. var (
  31. ErrNotFound = &ollama.Error{Status: 404, Code: "not_found"}
  32. ErrInternal = &ollama.Error{Status: 500, Code: "internal_error"}
  33. ErrMethodNotAllowed = &ollama.Error{Status: 405, Code: "method_not_allowed"}
  34. )
  35. type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
  36. func Serve(h HandlerFunc, w http.ResponseWriter, r *http.Request) {
  37. if err := h(w, r); err != nil {
  38. // TODO: take a slog.Logger
  39. log.Printf("error: %v", err)
  40. var oe *ollama.Error
  41. if !errors.As(err, &oe) {
  42. oe = ErrInternal
  43. }
  44. oe.Status = cmp.Or(oe.Status, 400)
  45. w.WriteHeader(oe.Status)
  46. if err := EncodeJSON(w, oe); err != nil {
  47. log.Printf("error encoding error: %v", err)
  48. }
  49. }
  50. }
  51. func DecodeUserJSON[T any](field string, r io.Reader) (*T, error) {
  52. v, err := DecodeJSON[T](r)
  53. // Handle common JSON syntax errors
  54. var e *json.SyntaxError
  55. if errors.As(err, &e) {
  56. return nil, Invalid(field, "", e.Error())
  57. }
  58. // Handle type errors
  59. var se *json.UnmarshalTypeError
  60. if errors.As(err, &se) {
  61. return nil, Invalid(field, se.Value, "expected %s", se.Type)
  62. }
  63. // Return v and err as they were.
  64. return v, err
  65. }
  66. func DecodeJSON[T any](r io.Reader) (*T, error) {
  67. var v *T
  68. if err := json.NewDecoder(r).Decode(&v); err != nil {
  69. var zero T
  70. return &zero, err
  71. }
  72. return v, nil
  73. }
  74. func EncodeJSON(w io.Writer, v any) error {
  75. return json.NewEncoder(w).Encode(v)
  76. }