Files

84 lines
1.9 KiB
Go

package domain
import (
"fmt"
"unicode/utf8"
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
)
const MaxProtocolDetailBytes = 4 * 1024
// Error is the transport-neutral form of an RVBox structured failure.
type Error struct {
Code rvboxv1.ControlError_Code
Message string
Retryable bool
IssueUUID string
Cause error
}
func (e *Error) Error() string {
if e.IssueUUID == "" {
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
return fmt.Sprintf("%s (%s): %s", e.Code, e.IssueUUID, e.Message)
}
func (e *Error) Unwrap() error { return e.Cause }
// NewError applies the stable retryability policy and bounds wire-visible text.
func NewError(code rvboxv1.ControlError_Code, message, issueUUID string) *Error {
return &Error{
Code: code,
Message: boundUTF8(message, MaxProtocolDetailBytes),
Retryable: DefaultRetryable(code),
IssueUUID: issueUUID,
}
}
func (e *Error) WithCause(cause error) *Error {
e.Cause = cause
return e
}
func (e *Error) ControlError() *rvboxv1.ControlError {
return &rvboxv1.ControlError{
Code: e.Code,
Message: boundUTF8(e.Message, MaxProtocolDetailBytes),
Retryable: e.Retryable,
IssueUuid: e.IssueUUID,
}
}
func DefaultRetryable(code rvboxv1.ControlError_Code) bool {
switch code {
case rvboxv1.ControlError_OFFLINE,
rvboxv1.ControlError_CAPACITY_EXHAUSTED,
rvboxv1.ControlError_TRANSIENT,
rvboxv1.ControlError_INTERNAL:
return true
default:
return false
}
}
func NewExecutionContextUnavailable(message, issueUUID string) *Error {
return NewError(rvboxv1.ControlError_CODE_EXECUTION_CONTEXT_UNAVAILABLE, message, issueUUID)
}
func NewElevationUnavailable(message, issueUUID string) *Error {
return NewError(rvboxv1.ControlError_CODE_ELEVATION_UNAVAILABLE, message, issueUUID)
}
func boundUTF8(value string, limit int) string {
if len(value) <= limit {
return value
}
value = value[:limit]
for !utf8.ValidString(value) {
value = value[:len(value)-1]
}
return value
}