errors.mx raw

   1  // Package errors provides domain-specific error types for structured
   2  // error handling with machine-readable codes and categories.
   3  package errors
   4  
   5  import (
   6  )
   7  
   8  // DomainError is the base interface for all domain errors.
   9  type DomainError interface {
  10  	error
  11  	Code() []byte
  12  	Category() []byte
  13  	IsRetryable() bool
  14  }
  15  
  16  // Base provides common fields for domain errors.
  17  type Base struct {
  18  	code      []byte
  19  	category  []byte
  20  	message   []byte
  21  	retryable bool
  22  	cause     error
  23  }
  24  
  25  func (e *Base) Error() (s string) {
  26  	if e.cause != nil {
  27  		return e.message | ": " | e.cause.Error()
  28  	}
  29  	return string(e.message)
  30  }
  31  
  32  func (e *Base) String() (s string) { return e.Error() }
  33  
  34  func (e *Base) Code() (buf []byte) { return e.code }
  35  func (e *Base) Category() (buf []byte) { return e.category }
  36  func (e *Base) IsRetryable() (ok bool) { return e.retryable }
  37  func (e *Base) Unwrap() (err error) { return e.cause }
  38  
  39  func (e *Base) WithCause(cause error) (b *Base) {
  40  	return &Base{
  41  		code: e.code, category: e.category,
  42  		message: e.message, retryable: e.retryable, cause: cause,
  43  	}
  44  }
  45  
  46  func (e *Base) WithMessage(msg []byte) (b *Base) {
  47  	return &Base{
  48  		code: e.code, category: e.category,
  49  		message: msg, retryable: e.retryable, cause: e.cause,
  50  	}
  51  }
  52  
  53  // --- Validation ---
  54  
  55  type ValidationError struct {
  56  	Base
  57  	Field []byte
  58  }
  59  
  60  func NewValidationError(code, field, message []byte) (v *ValidationError) {
  61  	return &ValidationError{
  62  		Base:  Base{code: code, category: []byte("validation"), message: message},
  63  		Field: field,
  64  	}
  65  }
  66  
  67  func (e *ValidationError) WithField(field []byte) (v *ValidationError) {
  68  	return &ValidationError{Base: e.Base, Field: field}
  69  }
  70  
  71  var (
  72  	ErrInvalidEventID       *ValidationError
  73  	ErrInvalidSignature     *ValidationError
  74  	ErrFutureTimestamp      *ValidationError
  75  	ErrPastTimestamp        *ValidationError
  76  	ErrUppercaseHex         *ValidationError
  77  	ErrProtectedTagMismatch *ValidationError
  78  	ErrInvalidJSON          *ValidationError
  79  	ErrEventTooLarge        *ValidationError
  80  	ErrInvalidKind          *ValidationError
  81  	ErrMissingTag           *ValidationError
  82  	ErrInvalidTagValue      *ValidationError
  83  )
  84  
  85  // --- Authorization ---
  86  
  87  type AuthorizationError struct {
  88  	Base
  89  	Pubkey      []byte
  90  	AccessLevel []byte
  91  	RequireAuth bool
  92  }
  93  
  94  func (e *AuthorizationError) NeedsAuth() (ok bool) { return e.RequireAuth }
  95  
  96  func NewAuthRequired(reason []byte) (a *AuthorizationError) {
  97  	return &AuthorizationError{
  98  		Base:        Base{code: []byte("AUTH_REQUIRED"), category: []byte("authorization"), message: reason},
  99  		RequireAuth: true,
 100  	}
 101  }
 102  
 103  func NewAccessDenied(level, reason []byte) (a *AuthorizationError) {
 104  	return &AuthorizationError{
 105  		Base:        Base{code: []byte("ACCESS_DENIED"), category: []byte("authorization"), message: reason},
 106  		AccessLevel: level,
 107  	}
 108  }
 109  
 110  func (e *AuthorizationError) WithPubkey(pubkey []byte) (a *AuthorizationError) {
 111  	return &AuthorizationError{
 112  		Base: e.Base, Pubkey: pubkey,
 113  		AccessLevel: e.AccessLevel, RequireAuth: e.RequireAuth,
 114  	}
 115  }
 116  
 117  var (
 118  	ErrAuthRequired       *AuthorizationError
 119  	ErrBanned             *AuthorizationError
 120  	ErrIPBlocked          *AuthorizationError
 121  	ErrNotFollowed        *AuthorizationError
 122  	ErrNotMember          *AuthorizationError
 123  	ErrInsufficientAccess *AuthorizationError
 124  )
 125  
 126  // --- Processing ---
 127  
 128  type ProcessingError struct {
 129  	Base
 130  	EventID []byte
 131  	Kind    uint16
 132  }
 133  
 134  func NewProcessingError(code, message []byte, retryable bool) (p *ProcessingError) {
 135  	return &ProcessingError{
 136  		Base: Base{code: code, category: []byte("processing"), message: message, retryable: retryable},
 137  	}
 138  }
 139  
 140  func (e *ProcessingError) WithEventID(id []byte) (p *ProcessingError) {
 141  	return &ProcessingError{Base: e.Base, EventID: id, Kind: e.Kind}
 142  }
 143  
 144  func (e *ProcessingError) WithKind(kind uint16) (p *ProcessingError) {
 145  	return &ProcessingError{Base: e.Base, EventID: e.EventID, Kind: kind}
 146  }
 147  
 148  var (
 149  	ErrDuplicate          *ProcessingError
 150  	ErrReplaceNotAllowed  *ProcessingError
 151  	ErrDeletedEvent       *ProcessingError
 152  	ErrEphemeralNotStored *ProcessingError
 153  	ErrRateLimited        *ProcessingError
 154  )
 155  
 156  // --- Policy ---
 157  
 158  type PolicyError struct {
 159  	Base
 160  	RuleName []byte
 161  	Action   []byte
 162  }
 163  
 164  func NewPolicyBlocked(ruleName, reason []byte) (p *PolicyError) {
 165  	return &PolicyError{
 166  		Base:     Base{code: []byte("POLICY_BLOCKED"), category: []byte("policy"), message: reason},
 167  		RuleName: ruleName, Action: []byte("block"),
 168  	}
 169  }
 170  
 171  func NewPolicyRejected(ruleName, reason []byte) (p *PolicyError) {
 172  	return &PolicyError{
 173  		Base:     Base{code: []byte("POLICY_REJECTED"), category: []byte("policy"), message: reason},
 174  		RuleName: ruleName, Action: []byte("reject"),
 175  	}
 176  }
 177  
 178  var (
 179  	ErrKindBlocked    *PolicyError
 180  	ErrPubkeyBlocked  *PolicyError
 181  	ErrContentBlocked *PolicyError
 182  )
 183  
 184  // --- Storage ---
 185  
 186  type StorageError struct{ Base }
 187  
 188  func NewStorageError(code, message []byte, cause error, retryable bool) (s *StorageError) {
 189  	return &StorageError{Base: Base{code: code, category: []byte("storage"), message: message, cause: cause, retryable: retryable}}
 190  }
 191  
 192  var (
 193  	ErrDatabaseUnavailable *StorageError
 194  	ErrWriteTimeout        *StorageError
 195  	ErrReadTimeout         *StorageError
 196  	ErrStorageFull         *StorageError
 197  	ErrCorruptedData       *StorageError
 198  )
 199  
 200  // --- Service ---
 201  
 202  type ServiceError struct {
 203  	Base
 204  	ServiceName []byte
 205  }
 206  
 207  func NewServiceError(code, service, message []byte, retryable bool) (s *ServiceError) {
 208  	return &ServiceError{
 209  		Base:        Base{code: code, category: []byte("service"), message: message, retryable: retryable},
 210  		ServiceName: service,
 211  	}
 212  }
 213  
 214  var (
 215  	ErrServiceUnavailable *ServiceError
 216  	ErrServiceTimeout     *ServiceError
 217  	ErrServiceOverloaded  *ServiceError
 218  )
 219  
 220  func init() {
 221  	ErrInvalidEventID = NewValidationError([]byte("INVALID_ID"), []byte("id"), []byte("event ID does not match computed hash"))
 222  	ErrInvalidSignature = NewValidationError([]byte("INVALID_SIG"), []byte("sig"), []byte("signature verification failed"))
 223  	ErrFutureTimestamp = NewValidationError([]byte("FUTURE_TS"), []byte("created_at"), []byte("timestamp too far in future"))
 224  	ErrPastTimestamp = NewValidationError([]byte("PAST_TS"), []byte("created_at"), []byte("timestamp too far in past"))
 225  	ErrUppercaseHex = NewValidationError([]byte("UPPERCASE_HEX"), []byte("id/pubkey"), []byte("hex values must be lowercase"))
 226  	ErrProtectedTagMismatch = NewValidationError([]byte("PROTECTED_TAG"), []byte("tags"), []byte("protected event can only be modified by author"))
 227  	ErrInvalidJSON = NewValidationError([]byte("INVALID_JSON"), nil, []byte("malformed JSON"))
 228  	ErrEventTooLarge = NewValidationError([]byte("EVENT_TOO_LARGE"), []byte("content"), []byte("event exceeds size limit"))
 229  	ErrInvalidKind = NewValidationError([]byte("INVALID_KIND"), []byte("kind"), []byte("event kind not allowed"))
 230  	ErrMissingTag = NewValidationError([]byte("MISSING_TAG"), []byte("tags"), []byte("required tag missing"))
 231  	ErrInvalidTagValue = NewValidationError([]byte("INVALID_TAG"), []byte("tags"), []byte("tag value validation failed"))
 232  	ErrAuthRequired = NewAuthRequired([]byte("authentication required"))
 233  	ErrBanned = &AuthorizationError{
 234  		Base: Base{code: []byte("BANNED"), category: []byte("authorization"), message: []byte("pubkey banned")},
 235  	}
 236  	ErrIPBlocked = &AuthorizationError{
 237  		Base: Base{code: []byte("IP_BLOCKED"), category: []byte("authorization"), message: []byte("IP address blocked")},
 238  	}
 239  	ErrNotFollowed = &AuthorizationError{
 240  		Base: Base{code: []byte("NOT_FOLLOWED"), category: []byte("authorization"), message: []byte("write access requires being followed by admin")},
 241  	}
 242  	ErrNotMember = &AuthorizationError{
 243  		Base: Base{code: []byte("NOT_MEMBER"), category: []byte("authorization"), message: []byte("membership required")},
 244  	}
 245  	ErrInsufficientAccess = &AuthorizationError{
 246  		Base: Base{code: []byte("INSUFFICIENT_ACCESS"), category: []byte("authorization"), message: []byte("insufficient access level")},
 247  	}
 248  	ErrDuplicate = NewProcessingError([]byte("DUPLICATE"), []byte("event already exists"), false)
 249  	ErrReplaceNotAllowed = NewProcessingError([]byte("REPLACE_DENIED"), []byte("cannot replace event from different author"), false)
 250  	ErrDeletedEvent = NewProcessingError([]byte("DELETED"), []byte("event has been deleted"), false)
 251  	ErrEphemeralNotStored = NewProcessingError([]byte("EPHEMERAL"), []byte("ephemeral events are not stored"), false)
 252  	ErrRateLimited = NewProcessingError([]byte("RATE_LIMITED"), []byte("rate limit exceeded"), true)
 253  	ErrKindBlocked = &PolicyError{
 254  		Base: Base{code: []byte("KIND_BLOCKED"), category: []byte("policy"), message: []byte("event kind not allowed by policy")},
 255  		Action: []byte("block"),
 256  	}
 257  	ErrPubkeyBlocked = &PolicyError{
 258  		Base: Base{code: []byte("PUBKEY_BLOCKED"), category: []byte("policy"), message: []byte("pubkey blocked by policy")},
 259  		Action: []byte("block"),
 260  	}
 261  	ErrContentBlocked = &PolicyError{
 262  		Base: Base{code: []byte("CONTENT_BLOCKED"), category: []byte("policy"), message: []byte("content blocked by policy")},
 263  		Action: []byte("block"),
 264  	}
 265  	ErrDatabaseUnavailable = NewStorageError([]byte("DB_UNAVAILABLE"), []byte("database not available"), nil, true)
 266  	ErrWriteTimeout = NewStorageError([]byte("WRITE_TIMEOUT"), []byte("write operation timed out"), nil, true)
 267  	ErrReadTimeout = NewStorageError([]byte("READ_TIMEOUT"), []byte("read operation timed out"), nil, true)
 268  	ErrStorageFull = NewStorageError([]byte("STORAGE_FULL"), []byte("storage capacity exceeded"), nil, false)
 269  	ErrCorruptedData = NewStorageError([]byte("CORRUPTED_DATA"), []byte("data corruption detected"), nil, false)
 270  	ErrServiceUnavailable = NewServiceError([]byte("SERVICE_UNAVAILABLE"), nil, []byte("service temporarily unavailable"), true)
 271  	ErrServiceTimeout = NewServiceError([]byte("SERVICE_TIMEOUT"), nil, []byte("service request timed out"), true)
 272  	ErrServiceOverloaded = NewServiceError([]byte("SERVICE_OVERLOADED"), nil, []byte("service is overloaded"), true)
 273  }
 274  
 275  // --- Helpers ---
 276  
 277  func Is(err error, target DomainError) (ok bool) {
 278  	if de, match := err.(DomainError); match {
 279  		return bytesEqual(de.Code(), target.Code())
 280  	}
 281  	return false
 282  }
 283  
 284  func Code(err error) (buf []byte) {
 285  	if de, match := err.(DomainError); match {
 286  		return de.Code()
 287  	}
 288  	return nil
 289  }
 290  
 291  func Category(err error) (buf []byte) {
 292  	if de, match := err.(DomainError); match {
 293  		return de.Category()
 294  	}
 295  	return []byte("unknown")
 296  }
 297  
 298  func IsRetryable(err error) (ok bool) {
 299  	if de, match := err.(DomainError); match {
 300  		return de.IsRetryable()
 301  	}
 302  	return false
 303  }
 304  
 305  func IsValidation(err error) (ok bool) { _, match := err.(*ValidationError); return match }
 306  func IsAuthorization(err error) (ok bool) { _, match := err.(*AuthorizationError); return match }
 307  func IsProcessing(err error) (ok bool) { _, match := err.(*ProcessingError); return match }
 308  func IsPolicy(err error) (ok bool) { _, match := err.(*PolicyError); return match }
 309  func IsStorage(err error) (ok bool) { _, match := err.(*StorageError); return match }
 310  
 311  func NeedsAuth(err error) (ok bool) {
 312  	if ae, match := err.(*AuthorizationError); match {
 313  		return ae.NeedsAuth()
 314  	}
 315  	return false
 316  }
 317  
 318  func bytesEqual(a, b []byte) (ok bool) {
 319  	if len(a) != len(b) {
 320  		return false
 321  	}
 322  	for i := range a {
 323  		if a[i] != b[i] {
 324  			return false
 325  		}
 326  	}
 327  	return true
 328  }
 329