summary refs log tree commit diff
path: root/vendor/maunium.net/go/mautrix/error.go
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-10-25 15:55:50 +0200
committerEmile <git@emile.space>2024-10-25 15:55:50 +0200
commitc90f36e3dd179d2de96f4f5fe38d8dc9a9de6dfe (patch)
tree89e9afb41c5bf76f48cfb09305a2d3db8d302b06 /vendor/maunium.net/go/mautrix/error.go
parent98bbb0f559a8883bc47bae80607dbe326a448e61 (diff)
vendor HEAD main
Diffstat (limited to 'vendor/maunium.net/go/mautrix/error.go')
-rw-r--r--vendor/maunium.net/go/mautrix/error.go184
1 files changed, 184 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/mautrix/error.go b/vendor/maunium.net/go/mautrix/error.go
new file mode 100644
index 0000000..a4ba985
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/error.go
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mautrix
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+
+	"go.mau.fi/util/exhttp"
+	"golang.org/x/exp/maps"
+)
+
+// Common error codes from https://matrix.org/docs/spec/client_server/latest#api-standards
+//
+// Can be used with errors.Is() to check the response code without casting the error:
+//
+//	err := client.Sync()
+//	if errors.Is(err, MUnknownToken) {
+//		// logout
+//	}
+var (
+	// Generic error for when the server encounters an error and it does not have a more specific error code.
+	// Note that `errors.Is` will check the error message rather than code for M_UNKNOWNs.
+	MUnknown = RespError{ErrCode: "M_UNKNOWN", StatusCode: http.StatusInternalServerError}
+	// Forbidden access, e.g. joining a room without permission, failed login.
+	MForbidden = RespError{ErrCode: "M_FORBIDDEN", StatusCode: http.StatusForbidden}
+	// Unrecognized request, e.g. the endpoint does not exist or is not implemented.
+	MUnrecognized = RespError{ErrCode: "M_UNRECOGNIZED", StatusCode: http.StatusNotFound}
+	// The access token specified was not recognised.
+	MUnknownToken = RespError{ErrCode: "M_UNKNOWN_TOKEN", StatusCode: http.StatusUnauthorized}
+	// No access token was specified for the request.
+	MMissingToken = RespError{ErrCode: "M_MISSING_TOKEN", StatusCode: http.StatusUnauthorized}
+	// Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys.
+	MBadJSON = RespError{ErrCode: "M_BAD_JSON", StatusCode: http.StatusBadRequest}
+	// Request did not contain valid JSON.
+	MNotJSON = RespError{ErrCode: "M_NOT_JSON", StatusCode: http.StatusBadRequest}
+	// No resource was found for this request.
+	MNotFound = RespError{ErrCode: "M_NOT_FOUND", StatusCode: http.StatusNotFound}
+	// Too many requests have been sent in a short period of time. Wait a while then try again.
+	MLimitExceeded = RespError{ErrCode: "M_LIMIT_EXCEEDED", StatusCode: http.StatusTooManyRequests}
+	// The user ID associated with the request has been deactivated.
+	// Typically for endpoints that prove authentication, such as /login.
+	MUserDeactivated = RespError{ErrCode: "M_USER_DEACTIVATED"}
+	// Encountered when trying to register a user ID which has been taken.
+	MUserInUse = RespError{ErrCode: "M_USER_IN_USE", StatusCode: http.StatusBadRequest}
+	// Encountered when trying to register a user ID which is not valid.
+	MInvalidUsername = RespError{ErrCode: "M_INVALID_USERNAME", StatusCode: http.StatusBadRequest}
+	// Sent when the room alias given to the createRoom API is already in use.
+	MRoomInUse = RespError{ErrCode: "M_ROOM_IN_USE", StatusCode: http.StatusBadRequest}
+	// The state change requested cannot be performed, such as attempting to unban a user who is not banned.
+	MBadState = RespError{ErrCode: "M_BAD_STATE"}
+	// The request or entity was too large.
+	MTooLarge = RespError{ErrCode: "M_TOO_LARGE", StatusCode: http.StatusRequestEntityTooLarge}
+	// The resource being requested is reserved by an application service, or the application service making the request has not created the resource.
+	MExclusive = RespError{ErrCode: "M_EXCLUSIVE", StatusCode: http.StatusBadRequest}
+	// The client's request to create a room used a room version that the server does not support.
+	MUnsupportedRoomVersion = RespError{ErrCode: "M_UNSUPPORTED_ROOM_VERSION"}
+	// The client attempted to join a room that has a version the server does not support.
+	// Inspect the room_version property of the error response for the room's version.
+	MIncompatibleRoomVersion = RespError{ErrCode: "M_INCOMPATIBLE_ROOM_VERSION"}
+	// The client specified a parameter that has the wrong value.
+	MInvalidParam = RespError{ErrCode: "M_INVALID_PARAM", StatusCode: http.StatusBadRequest}
+
+	MURLNotSet         = RespError{ErrCode: "M_URL_NOT_SET"}
+	MBadStatus         = RespError{ErrCode: "M_BAD_STATUS"}
+	MConnectionTimeout = RespError{ErrCode: "M_CONNECTION_TIMEOUT"}
+	MConnectionFailed  = RespError{ErrCode: "M_CONNECTION_FAILED"}
+)
+
+// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
+type HTTPError struct {
+	Request      *http.Request
+	Response     *http.Response
+	ResponseBody string
+
+	WrappedError error
+	RespError    *RespError
+	Message      string
+}
+
+func (e HTTPError) Is(err error) bool {
+	return (e.RespError != nil && errors.Is(e.RespError, err)) || (e.WrappedError != nil && errors.Is(e.WrappedError, err))
+}
+
+func (e HTTPError) IsStatus(code int) bool {
+	return e.Response != nil && e.Response.StatusCode == code
+}
+
+func (e HTTPError) Error() string {
+	if e.WrappedError != nil {
+		return fmt.Sprintf("%s: %v", e.Message, e.WrappedError)
+	} else if e.RespError != nil {
+		return fmt.Sprintf("failed to %s %s: %s (HTTP %d): %s", e.Request.Method, e.Request.URL.Path,
+			e.RespError.ErrCode, e.Response.StatusCode, e.RespError.Err)
+	} else {
+		msg := fmt.Sprintf("failed to %s %s: HTTP %d", e.Request.Method, e.Request.URL.Path, e.Response.StatusCode)
+		if len(e.ResponseBody) > 0 {
+			msg = fmt.Sprintf("%s: %s", msg, e.ResponseBody)
+		}
+		return msg
+	}
+}
+
+func (e HTTPError) Unwrap() error {
+	if e.WrappedError != nil {
+		return e.WrappedError
+	} else if e.RespError != nil {
+		return *e.RespError
+	}
+	return nil
+}
+
+// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
+// See https://spec.matrix.org/v1.2/client-server-api/#api-standards
+type RespError struct {
+	ErrCode   string
+	Err       string
+	ExtraData map[string]any
+
+	StatusCode int
+}
+
+func (e *RespError) UnmarshalJSON(data []byte) error {
+	err := json.Unmarshal(data, &e.ExtraData)
+	if err != nil {
+		return err
+	}
+	e.ErrCode, _ = e.ExtraData["errcode"].(string)
+	e.Err, _ = e.ExtraData["error"].(string)
+	return nil
+}
+
+func (e *RespError) MarshalJSON() ([]byte, error) {
+	data := maps.Clone(e.ExtraData)
+	if data == nil {
+		data = make(map[string]any)
+	}
+	data["errcode"] = e.ErrCode
+	data["error"] = e.Err
+	return json.Marshal(data)
+}
+
+func (e RespError) Write(w http.ResponseWriter) {
+	statusCode := e.StatusCode
+	if statusCode == 0 {
+		statusCode = http.StatusInternalServerError
+	}
+	exhttp.WriteJSONResponse(w, statusCode, &e)
+}
+
+func (e RespError) WithMessage(msg string, args ...any) RespError {
+	if len(args) > 0 {
+		msg = fmt.Sprintf(msg, args...)
+	}
+	e.Err = msg
+	return e
+}
+
+func (e RespError) WithStatus(status int) RespError {
+	e.StatusCode = status
+	return e
+}
+
+// Error returns the errcode and error message.
+func (e RespError) Error() string {
+	return e.ErrCode + ": " + e.Err
+}
+
+func (e RespError) Is(err error) bool {
+	e2, ok := err.(RespError)
+	if !ok {
+		return false
+	}
+	if e.ErrCode == "M_UNKNOWN" && e2.ErrCode == "M_UNKNOWN" {
+		return e.Err == e2.Err
+	}
+	return e2.ErrCode == e.ErrCode
+}