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, ¬FoundErr) || 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}