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 27// ValidationError represents a validation error with field context 28type ValidationError struct { 29 Field string 30 Message string 31} 32 33func (e *ValidationError) Error() string { 34 return fmt.Sprintf("validation error (%s): %s", e.Field, e.Message) 35} 36 37// NewValidationError creates a new validation error 38func NewValidationError(field, message string) error { 39 return &ValidationError{ 40 Field: field, 41 Message: message, 42 } 43} 44 45// IsValidationError checks if error is a validation error 46func IsValidationError(err error) bool { 47 var valErr *ValidationError 48 return errors.As(err, &valErr) 49} 50 51// ContentRuleViolation represents a violation of community content rules 52// (Deferred to Beta - included here for future compatibility) 53type ContentRuleViolation struct { 54 Rule string // e.g., "requireText", "allowedEmbedTypes" 55 Message string // Human-readable explanation 56} 57 58func (e *ContentRuleViolation) Error() string { 59 return fmt.Sprintf("content rule violation (%s): %s", e.Rule, e.Message) 60} 61 62// NewContentRuleViolation creates a new content rule violation error 63func NewContentRuleViolation(rule, message string) error { 64 return &ContentRuleViolation{ 65 Rule: rule, 66 Message: message, 67 } 68} 69 70// IsContentRuleViolation checks if error is a content rule violation 71func IsContentRuleViolation(err error) bool { 72 var violation *ContentRuleViolation 73 return errors.As(err, &violation) 74} 75 76// NotFoundError represents a resource not found error 77type NotFoundError struct { 78 Resource string // e.g., "post", "community" 79 ID string // Resource identifier 80} 81 82func (e *NotFoundError) Error() string { 83 return fmt.Sprintf("%s not found: %s", e.Resource, e.ID) 84} 85 86// NewNotFoundError creates a new not found error 87func NewNotFoundError(resource, id string) error { 88 return &NotFoundError{ 89 Resource: resource, 90 ID: id, 91 } 92} 93 94// IsNotFound checks if error is a not found error 95func IsNotFound(err error) bool { 96 var notFoundErr *NotFoundError 97 return errors.As(err, &notFoundErr) || err == ErrCommunityNotFound || err == ErrNotFound 98} 99 100// IsConflict checks if error is due to duplicate/conflict 101func IsConflict(err error) bool { 102 if err == nil { 103 return false 104 } 105 // Check for common conflict indicators in error message 106 errStr := err.Error() 107 return contains(errStr, "already indexed") || 108 contains(errStr, "duplicate key") || 109 contains(errStr, "already exists") 110} 111 112func contains(s, substr string) bool { 113 return len(s) >= len(substr) && anySubstring(s, substr) 114} 115 116func anySubstring(s, substr string) bool { 117 for i := 0; i <= len(s)-len(substr); i++ { 118 if s[i:i+len(substr)] == substr { 119 return true 120 } 121 } 122 return false 123}