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