Something went wrong. Try again.
A community based topic aggregation platform built on atproto
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169// Package errors defines the error types shared across every Coves domain// package.//// Each domain used to declare its own ValidationError, NotFoundError, and// friends. Because those were distinct Go types with identical shapes, a// handler could not ask "is this a validation failure?" in one place — it had// to call posts.IsValidationError, then communities.IsValidationError, then// aggregators.IsValidationError, and any domain a handler forgot silently fell// through to a 500.//// Domain packages now alias these types://// type ValidationError = coreerrors.ValidationError//// A Go type alias is the *same* type, not a similar one, so the ValidationError// of every domain that aliases it is matched by a single errors.As at the// boundary, while each package keeps its own familiar constructors and// predicates.//// Six packages participate today: posts, communities, aggregators,// communityFeeds, discover, and timeline. Domains that signal validation// failures with sentinel errors instead — comments, for one — still need their// own predicate at the boundary.package errors
import ( "errors" "fmt")
// Shared sentinel errors, kept to the two the typed errors below bridge to.// Domain packages define their own more specific sentinels; speculative ones// here would rebuild the duplicated error surface this package exists to// remove.var ( ErrNotFound = errors.New("resource not found") ErrAlreadyExists = errors.New("resource already exists")
// ErrReinstateFailed marks the one indexing failure a caller must not shrug // off: an authenticated account's erasure marker could not be cleared. // // Every other failure while indexing a login leaves the account where it // already was and is repaired by the next login or firehose event. This one // is not: the account authenticated, so it is entitled to come back, and // until the marker goes the ingestion gate drops everything it writes. A // caller that logs this and carries on hands the user a working session over // an account that silently publishes nothing. // // IT LIVES HERE FOR THE REASON THIS PACKAGE EXISTS. The error is raised in // internal/core/users and read in internal/atproto/oauth, and users already // imports oauth for its HTTP transport — so a sentinel declared in users // could never be referenced from the OAuth callback without an import // cycle. users aliases it, exactly as six domains alias ValidationError. ErrReinstateFailed = errors.New("failed to clear the account erasure marker"))
// ValidationError reports invalid input, with the offending field named.//// Error() is surfaced to clients as the human-readable message of an XRPC// error response, so it stays short and free of internal detail. The stable// machine-readable contract is the error *code* the handler chooses, not this// string.type ValidationError struct { // Field is the input that failed validation, named as the client named // it (a lexicon property, not an internal struct field). Field string
// Message explains what was wrong with it. Message string
// Err is the underlying cause, when there is one. Without it, wrapping a // typed error to add field context would flatten it to a string and make // the original sentinel unreachable from errors.Is. Err error}
func (e *ValidationError) Error() string { return fmt.Sprintf("%s: %s", e.Field, e.Message)}
// Unwrap exposes the underlying cause to errors.Is and errors.As.func (e *ValidationError) Unwrap() error { return e.Err}
// NewValidationError reports that field failed validation for the given// reason.func NewValidationError(field, message string) *ValidationError { return &ValidationError{Field: field, Message: message}}
// NewValidationErrorFrom adds field context to an existing error while keeping// cause matchable through errors.Is — which matters when cause is a sentinel// from a package like internal/validation.func NewValidationErrorFrom(field string, cause error) *ValidationError { return &ValidationError{ Field: field, Message: cause.Error(), Err: cause, }}
// IsValidationError reports whether err is, or wraps, a ValidationError.func IsValidationError(err error) bool { var validationErr *ValidationError return errors.As(err, &validationErr)}
// NotFoundError reports that a specific resource does not exist.type NotFoundError struct { // Resource is the kind of thing that was missing ("post", "community"). Resource string
// ID is the identifier that was looked up. ID string}
func (e *NotFoundError) Error() string { return fmt.Sprintf("%s not found: %s", e.Resource, e.ID)}
// Is makes every NotFoundError match the shared ErrNotFound sentinel, so a// caller can test for "missing" without caring which resource was missing.func (e *NotFoundError) Is(target error) bool { return target == ErrNotFound}
// NewNotFoundError reports that the identified resource does not exist.func NewNotFoundError(resource, id string) *NotFoundError { return &NotFoundError{Resource: resource, ID: id}}
// IsNotFound reports whether err is, or wraps, a NotFoundError.func IsNotFound(err error) bool { var notFoundErr *NotFoundError return errors.As(err, ¬FoundErr)}
// ConflictError reports that a resource already exists with the given value,// typically a uniqueness violation.type ConflictError struct { // Resource is the kind of thing that already existed ("community"). Resource string
// Field and Value identify the colliding value ("handle", "!go@coves"). Field string Value string}
func (e *ConflictError) Error() string { return fmt.Sprintf("%s with %s %q already exists", e.Resource, e.Field, e.Value)}
// Is makes every ConflictError match the shared ErrAlreadyExists sentinel.func (e *ConflictError) Is(target error) bool { return target == ErrAlreadyExists}
// NewConflictError reports a uniqueness violation on the given field.func NewConflictError(resource, field, value string) *ConflictError { return &ConflictError{Resource: resource, Field: field, Value: value}}
// IsConflict reports whether err is, or wraps, a ConflictError.func IsConflict(err error) bool { var conflictErr *ConflictError return errors.As(err, &conflictErr)}