A community based topic aggregation platform built on atproto
1package posts 2 3import ( 4 "errors" 5 "fmt" 6) 7 8// Sentinel errors for common post operations 9var ( 10 // ErrCommunityNotFound is returned when the community doesn't exist in AppView 11 ErrCommunityNotFound = errors.New("community not found") 12 13 // ErrNotAuthorized is returned when user isn't authorized to post in community 14 // (e.g., banned, private community without membership - Beta) 15 ErrNotAuthorized = errors.New("user not authorized to post in this community") 16 17 // ErrBanned is returned when user is banned from community (Beta) 18 ErrBanned = errors.New("user is banned from this community") 19 20 // ErrInvalidContent is returned for general content violations 21 ErrInvalidContent = errors.New("invalid post content") 22 23 // ErrNotFound is returned when a post is not found by URI 24 ErrNotFound = errors.New("post not found") 25 26 // ErrRateLimitExceeded is returned when an aggregator exceeds rate limits 27 ErrRateLimitExceeded = errors.New("rate limit exceeded") 28) 29 30// ValidationError represents a validation error with field context 31type ValidationError struct { 32 Field string 33 Message string 34} 35 36func (e *ValidationError) Error() string { 37 return fmt.Sprintf("validation error (%s): %s", e.Field, e.Message) 38} 39 40// NewValidationError creates a new validation error 41func NewValidationError(field, message string) error { 42 return &ValidationError{ 43 Field: field, 44 Message: message, 45 } 46} 47 48// IsValidationError checks if error is a validation error 49func IsValidationError(err error) bool { 50 var valErr *ValidationError 51 return errors.As(err, &valErr) 52} 53 54// ContentRuleViolation represents a violation of community content rules 55// (Deferred to Beta - included here for future compatibility) 56type ContentRuleViolation struct { 57 Rule string // e.g., "requireText", "allowedEmbedTypes" 58 Message string // Human-readable explanation 59} 60 61func (e *ContentRuleViolation) Error() string { 62 return fmt.Sprintf("content rule violation (%s): %s", e.Rule, e.Message) 63} 64 65// NewContentRuleViolation creates a new content rule violation error 66func NewContentRuleViolation(rule, message string) error { 67 return &ContentRuleViolation{ 68 Rule: rule, 69 Message: message, 70 } 71} 72 73// IsContentRuleViolation checks if error is a content rule violation 74func IsContentRuleViolation(err error) bool { 75 var violation *ContentRuleViolation 76 return errors.As(err, &violation) 77} 78 79// NotFoundError represents a resource not found error 80type NotFoundError struct { 81 Resource string // e.g., "post", "community" 82 ID string // Resource identifier 83} 84 85func (e *NotFoundError) Error() string { 86 return fmt.Sprintf("%s not found: %s", e.Resource, e.ID) 87} 88 89// NewNotFoundError creates a new not found error 90func NewNotFoundError(resource, id string) error { 91 return &NotFoundError{ 92 Resource: resource, 93 ID: id, 94 } 95} 96 97// IsNotFound checks if error is a not found error 98func IsNotFound(err error) bool { 99 var notFoundErr *NotFoundError 100 return errors.As(err, &notFoundErr) || err == ErrCommunityNotFound || err == ErrNotFound 101} 102 103// IsConflict checks if error is due to duplicate/conflict 104func IsConflict(err error) bool { 105 if err == nil { 106 return false 107 } 108 // Check for common conflict indicators in error message 109 errStr := err.Error() 110 return contains(errStr, "already indexed") || 111 contains(errStr, "duplicate key") || 112 contains(errStr, "already exists") 113} 114 115func contains(s, substr string) bool { 116 return len(s) >= len(substr) && anySubstring(s, substr) 117} 118 119func anySubstring(s, substr string) bool { 120 for i := 0; i <= len(s)-len(substr); i++ { 121 if s[i:i+len(substr)] == substr { 122 return true 123 } 124 } 125 return false 126}