diff --git a/internal/errkind/errkind.go b/internal/errkind/errkind.go new file mode 100644 index 0000000..74e9099 --- /dev/null +++ b/internal/errkind/errkind.go @@ -0,0 +1,42 @@ +// Package errkind reduces an error to a short kind that is safe to log, store and show. +// +// An error's own text can hold a provider's response body (a gateway that echoes the +// prompt), a file path (ffmpeg names the spool file), or a word from a private +// transcript. None of that may reach a log line, a database column or a page about a +// private recording (ADR-033: "typed, never a provider's string"; ADR-035). Of returns +// only a fixed vocabulary, so it can never carry any. +package errkind + +import ( + "context" + "errors" + "fmt" + "net" +) + +// HTTPStatuser is implemented by errors that are a non-2xx answer from a service. +type HTTPStatuser interface{ HTTPStatus() int } + +// Of is the kind of err: "timeout", "canceled", "http_", "network", or "error". +// It is empty for nil. +func Of(err error) string { + if err == nil { + return "" + } + var hs HTTPStatuser + var ne net.Error + switch { + case errors.Is(err, context.DeadlineExceeded): + return "timeout" + case errors.Is(err, context.Canceled): + return "canceled" + case errors.As(err, &hs): + return fmt.Sprintf("http_%d", hs.HTTPStatus()) + case errors.As(err, &ne): + if ne.Timeout() { + return "timeout" + } + return "network" + } + return "error" +}