diff --git a/bin/gemget/main.go b/bin/gemget/main.go index be48ca5..129e739 100644 --- a/bin/gemget/main.go +++ b/bin/gemget/main.go @@ -8,10 +8,10 @@ import ( "gemini-grc/common/snapshot" _url "gemini-grc/common/url" "gemini-grc/config" - "gemini-grc/errors" "gemini-grc/gemini" "gemini-grc/gopher" "gemini-grc/logging" + "github.com/antanst/go_errors" ) func main() { @@ -26,7 +26,7 @@ func main() { func runApp() error { if len(os.Args) != 2 { - return errors.NewError(fmt.Errorf("missing URL to visit")) + return go_errors.NewError(fmt.Errorf("missing URL to visit")) } url := os.Args[1] var s *snapshot.Snapshot @@ -36,7 +36,7 @@ func runApp() error { } else if _url.IsGopherURL(url) { s, err = gopher.Visit(url) } else { - return errors.NewFatalError(fmt.Errorf("not a Gemini or Gopher URL")) + return go_errors.NewFatalError(fmt.Errorf("not a Gemini or Gopher URL")) } if err != nil { return err diff --git a/common/blackList/blacklist.go b/common/blackList/blacklist.go index ed4f159..43894c9 100644 --- a/common/blackList/blacklist.go +++ b/common/blackList/blacklist.go @@ -7,8 +7,8 @@ import ( "strings" "gemini-grc/config" - "gemini-grc/errors" "gemini-grc/logging" + "github.com/antanst/go_errors" ) var Blacklist []regexp.Regexp //nolint:gochecknoglobals @@ -21,7 +21,7 @@ func LoadBlacklist() error { data, err := os.ReadFile(config.CONFIG.BlacklistPath) if err != nil { Blacklist = []regexp.Regexp{} - return errors.NewError(fmt.Errorf("could not load Blacklist file: %w", err)) + return go_errors.NewError(fmt.Errorf("could not load Blacklist file: %w", err)) } lines := strings.Split(string(data), "\n") @@ -32,7 +32,7 @@ func LoadBlacklist() error { } regex, err := regexp.Compile(line) if err != nil { - return errors.NewError(fmt.Errorf("could not compile Blacklist line %s: %w", line, err)) + return go_errors.NewError(fmt.Errorf("could not compile Blacklist line %s: %w", line, err)) } Blacklist = append(Blacklist, *regex) diff --git a/common/errors/errors.go b/common/errors/errors.go index 2c88f29..bc306bc 100644 --- a/common/errors/errors.go +++ b/common/errors/errors.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - "gemini-grc/errors" + "github.com/antanst/go_errors" ) // HostError is an error encountered while @@ -30,7 +30,7 @@ func IsHostError(err error) bool { return false } var asError *HostError - return errors.As(err, &asError) + return go_errors.As(err, &asError) } // Sentinel errors used for their string message primarily. diff --git a/common/snapshot/snapshot.go b/common/snapshot/snapshot.go index c55248d..7fa999c 100644 --- a/common/snapshot/snapshot.go +++ b/common/snapshot/snapshot.go @@ -5,7 +5,7 @@ import ( "gemini-grc/common/linkList" commonUrl "gemini-grc/common/url" - "gemini-grc/errors" + "github.com/antanst/go_errors" "github.com/guregu/null/v5" ) @@ -27,7 +27,7 @@ type Snapshot struct { func SnapshotFromURL(u string, normalize bool) (*Snapshot, error) { url, err := commonUrl.ParseURL(u, "", normalize) if err != nil { - return nil, errors.NewError(err) + return nil, go_errors.NewError(err) } newSnapshot := Snapshot{ URL: *url, diff --git a/common/url/url.go b/common/url/url.go index 57bc4b2..4a3b0fb 100644 --- a/common/url/url.go +++ b/common/url/url.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "gemini-grc/errors" + "github.com/antanst/go_errors" ) type URL struct { @@ -29,7 +29,7 @@ func (u *URL) Scan(value interface{}) error { } b, ok := value.(string) if !ok { - return errors.NewFatalError(fmt.Errorf("database scan error: expected string, got %T", value)) + return go_errors.NewFatalError(fmt.Errorf("database scan error: expected string, got %T", value)) } parsedURL, err := ParseURL(b, "", false) if err != nil { @@ -82,7 +82,7 @@ func ParseURL(input string, descr string, normalize bool) (*URL, error) { } else { u, err = url.Parse(input) if err != nil { - return nil, errors.NewError(fmt.Errorf("error parsing URL: %w: %s", err, input)) + return nil, go_errors.NewError(fmt.Errorf("error parsing URL: %w: %s", err, input)) } } protocol := u.Scheme @@ -99,7 +99,7 @@ func ParseURL(input string, descr string, normalize bool) (*URL, error) { } port, err := strconv.Atoi(strPort) if err != nil { - return nil, errors.NewError(fmt.Errorf("error parsing URL: %w: %s", err, input)) + return nil, go_errors.NewError(fmt.Errorf("error parsing URL: %w: %s", err, input)) } full := fmt.Sprintf("%s://%s:%d%s", protocol, hostname, port, urlPath) // full field should also contain query params and url fragments @@ -145,13 +145,13 @@ func NormalizeURL(rawURL string) (*url.URL, error) { // Parse the URL u, err := url.Parse(rawURL) if err != nil { - return nil, errors.NewError(fmt.Errorf("error normalizing URL: %w: %s", err, rawURL)) + return nil, go_errors.NewError(fmt.Errorf("error normalizing URL: %w: %s", err, rawURL)) } if u.Scheme == "" { - return nil, errors.NewError(fmt.Errorf("error normalizing URL: No scheme: %s", rawURL)) + return nil, go_errors.NewError(fmt.Errorf("error normalizing URL: No scheme: %s", rawURL)) } if u.Host == "" { - return nil, errors.NewError(fmt.Errorf("error normalizing URL: No host: %s", rawURL)) + return nil, go_errors.NewError(fmt.Errorf("error normalizing URL: No host: %s", rawURL)) } // Convert scheme to lowercase @@ -275,7 +275,7 @@ func ExtractRedirectTargetFromHeader(currentURL URL, input string) (*URL, error) re := regexp.MustCompile(pattern) matches := re.FindStringSubmatch(input) if len(matches) < 2 { - return nil, errors.NewError(fmt.Errorf("error extracting redirect target from string %s", input)) + return nil, go_errors.NewError(fmt.Errorf("error extracting redirect target from string %s", input)) } newURL, err := DeriveAbsoluteURL(currentURL, matches[1]) if err != nil { diff --git a/common/worker.go b/common/worker.go index f974266..20a65d5 100644 --- a/common/worker.go +++ b/common/worker.go @@ -9,11 +9,11 @@ import ( "gemini-grc/common/snapshot" url2 "gemini-grc/common/url" _db "gemini-grc/db" - "gemini-grc/errors" "gemini-grc/gemini" "gemini-grc/gopher" "gemini-grc/hostPool" "gemini-grc/logging" + "github.com/antanst/go_errors" "github.com/guregu/null/v5" "github.com/jmoiron/sqlx" ) @@ -25,12 +25,12 @@ func CrawlOneURL(db *sqlx.DB, url *string) error { } if !url2.IsGeminiUrl(parsedURL.String()) && !url2.IsGopherURL(parsedURL.String()) { - return errors.NewError(fmt.Errorf("error parsing URL: not a Gemini or Gopher URL: %s", parsedURL.String())) + return go_errors.NewError(fmt.Errorf("error parsing URL: not a Gemini or Gopher URL: %s", parsedURL.String())) } tx, err := db.Beginx() if err != nil { - return errors.NewFatalError(err) + return go_errors.NewFatalError(err) } err = _db.InsertURL(tx, parsedURL.Full) @@ -49,9 +49,9 @@ func CrawlOneURL(db *sqlx.DB, url *string) error { // logging.LogError("Deadlock detected. Rolling back") // time.Sleep(time.Duration(10) * time.Second) // err := tx.Rollback() - // return errors.NewFatalError(err) + // return go_errors.NewFatalError(err) //} - return errors.NewFatalError(err) + return go_errors.NewFatalError(err) } logging.LogInfo("Done") return nil @@ -156,7 +156,7 @@ func workOnUrl(workerID int, tx *sqlx.Tx, url string) (err error) { isGemini := url2.IsGeminiUrl(s.URL.String()) isGopher := url2.IsGopherURL(s.URL.String()) if !isGemini && !isGopher { - return errors.NewError(fmt.Errorf("not a Gopher or Gemini URL: %s", s.URL.String())) + return go_errors.NewError(fmt.Errorf("not a Gopher or Gemini URL: %s", s.URL.String())) } if blackList.IsBlacklisted(s.URL.String()) { @@ -270,14 +270,14 @@ func haveWeVisitedURL(tx *sqlx.Tx, u string) (bool, error) { var result []bool err := tx.Select(&result, `SELECT TRUE FROM urls WHERE url=$1`, u) if err != nil { - return false, errors.NewFatalError(fmt.Errorf("database error: %w", err)) + return false, go_errors.NewFatalError(fmt.Errorf("database error: %w", err)) } if len(result) > 0 { return result[0], nil } err = tx.Select(&result, `SELECT TRUE FROM snapshots WHERE snapshots.url=$1`, u) if err != nil { - return false, errors.NewFatalError(fmt.Errorf("database error: %w", err)) + return false, go_errors.NewFatalError(fmt.Errorf("database error: %w", err)) } if len(result) > 0 { return result[0], nil diff --git a/db/db.go b/db/db.go index 97337c2..c4f208e 100644 --- a/db/db.go +++ b/db/db.go @@ -10,8 +10,8 @@ import ( "gemini-grc/common/snapshot" commonUrl "gemini-grc/common/url" "gemini-grc/config" - "gemini-grc/errors" "gemini-grc/logging" + "github.com/antanst/go_errors" _ "github.com/jackc/pgx/v5/stdlib" // PGX driver for PostgreSQL "github.com/jmoiron/sqlx" "github.com/lib/pq" @@ -29,17 +29,17 @@ func ConnectToDB() (*sqlx.DB, error) { // Create a connection pool db, err := sqlx.Open("pgx", connStr) if err != nil { - return nil, errors.NewFatalError(fmt.Errorf("unable to connect to database with URL %s: %w", connStr, err)) + return nil, go_errors.NewFatalError(fmt.Errorf("unable to connect to database with URL %s: %w", connStr, err)) } // TODO move PG_MAX_OPEN_CONNECTIONS to config env variables maxConnections, err := strconv.Atoi(os.Getenv("PG_MAX_OPEN_CONNECTIONS")) if err != nil { - return nil, errors.NewFatalError(fmt.Errorf("unable to set DB max connections: %w", err)) + return nil, go_errors.NewFatalError(fmt.Errorf("unable to set DB max connections: %w", err)) } db.SetMaxOpenConns(maxConnections) err = db.Ping() if err != nil { - return nil, errors.NewFatalError(fmt.Errorf("unable to ping database: %w", err)) + return nil, go_errors.NewFatalError(fmt.Errorf("unable to ping database: %w", err)) } logging.LogDebug("Connected to database") @@ -48,9 +48,9 @@ func ConnectToDB() (*sqlx.DB, error) { // IsDeadlockError checks if the error is a PostgreSQL deadlock error. func IsDeadlockError(err error) bool { - err = errors.Unwrap(err) + err = go_errors.Unwrap(err) var pqErr *pq.Error - if errors.As(err, &pqErr) { + if go_errors.As(err, &pqErr) { return pqErr.Code == "40P01" // PostgreSQL deadlock error code } return false @@ -60,7 +60,7 @@ func GetRandomUrls(tx *sqlx.Tx) ([]string, error) { var urls []string err := tx.Select(&urls, SQL_SELECT_RANDOM_URLS, config.CONFIG.WorkerBatchSize) if err != nil { - return nil, errors.NewFatalError(err) + return nil, go_errors.NewFatalError(err) } return urls, nil } @@ -70,7 +70,7 @@ func GetRandomUrlsWithBasePath(tx *sqlx.Tx) ([]string, error) { var urls []string err := tx.Select(&urls, SqlQuery, config.CONFIG.WorkerBatchSize) if err != nil { - return nil, errors.NewFatalError(err) + return nil, go_errors.NewFatalError(err) } return urls, nil } @@ -93,7 +93,7 @@ func InsertURL(tx *sqlx.Tx, url string) error { } _, err = tx.NamedExec(query, a) if err != nil { - return errors.NewFatalError(fmt.Errorf("cannot insert URL: database error %w URL %s", err, url)) + return go_errors.NewFatalError(fmt.Errorf("cannot insert URL: database error %w URL %s", err, url)) } return nil } @@ -103,7 +103,7 @@ func DeleteURL(tx *sqlx.Tx, url string) error { query := SQL_DELETE_URL _, err := tx.Exec(query, url) if err != nil { - return errors.NewFatalError(fmt.Errorf("cannot delete URL: database error %w URL %s", err, url)) + return go_errors.NewFatalError(fmt.Errorf("cannot delete URL: database error %w URL %s", err, url)) } return nil } @@ -112,7 +112,7 @@ func OverwriteSnapshot(tx *sqlx.Tx, s *snapshot.Snapshot) (err error) { if config.CONFIG.DryRun { marshalled, err := json.MarshalIndent(s, "", " ") if err != nil { - return errors.NewFatalError(fmt.Errorf("JSON serialization error for %v", s)) + return go_errors.NewFatalError(fmt.Errorf("JSON serialization error for %v", s)) } logging.LogDebug("Would upsert snapshot %s", marshalled) return nil @@ -120,19 +120,19 @@ func OverwriteSnapshot(tx *sqlx.Tx, s *snapshot.Snapshot) (err error) { query := SQL_UPSERT_SNAPSHOT rows, err := tx.NamedQuery(query, s) if err != nil { - return errors.NewFatalError(fmt.Errorf("cannot overwrite snapshot: %w", err)) + return go_errors.NewFatalError(fmt.Errorf("cannot overwrite snapshot: %w", err)) } defer func() { _err := rows.Close() if err == nil && _err != nil { - err = errors.NewFatalError(fmt.Errorf("cannot overwrite snapshot: error closing rows: %w", err)) + err = go_errors.NewFatalError(fmt.Errorf("cannot overwrite snapshot: error closing rows: %w", err)) } }() if rows.Next() { var returnedID int err = rows.Scan(&returnedID) if err != nil { - return errors.NewFatalError(fmt.Errorf("cannot overwrite snapshot: error scanning rows: %w", err)) + return go_errors.NewFatalError(fmt.Errorf("cannot overwrite snapshot: error scanning rows: %w", err)) } s.ID = returnedID } diff --git a/errors/errors.go b/errors/errors.go deleted file mode 100644 index dfe24ad..0000000 --- a/errors/errors.go +++ /dev/null @@ -1,104 +0,0 @@ -package errors - -import ( - "errors" - "fmt" - "runtime" - "strings" -) - -type fatal interface { - Fatal() bool -} - -func IsFatal(err error) bool { - var e fatal - ok := As(err, &e) - return ok && e.Fatal() -} - -func As(err error, target any) bool { - return errors.As(err, target) -} - -func Is(err, target error) bool { - return errors.Is(err, target) -} - -func Unwrap(err error) error { - return errors.Unwrap(err) -} - -type Error struct { - Err error - Stack string - fatal bool -} - -func (e *Error) Error() string { - var sb strings.Builder - sb.WriteString(fmt.Sprintf("%v\n", e.Err)) - sb.WriteString(fmt.Sprintf("Stack Trace:\n%s", e.Stack)) - return sb.String() -} - -func (e *Error) Fatal() bool { - return e.fatal -} - -func (e *Error) Unwrap() error { - return e.Err -} - -func NewError(err error) error { - if err == nil { - return nil - } - - // Check if it's already of our own - // Error type, so we don't add stack twice. - var asError *Error - if errors.As(err, &asError) { - return err - } - - // has the stack trace - var stack strings.Builder - buf := make([]uintptr, 50) - n := runtime.Callers(2, buf) - frames := runtime.CallersFrames(buf[:n]) - - // Format the stack trace - for { - frame, more := frames.Next() - // Skip runtime and standard library frames - if !strings.Contains(frame.File, "runtime/") { - stack.WriteString(fmt.Sprintf("\t%s:%d - %s\n", frame.File, frame.Line, frame.Function)) - } - if !more { - break - } - } - - return &Error{ - Err: err, - Stack: stack.String(), - } -} - -func NewFatalError(err error) error { - if err == nil { - return nil - } - - // Check if it's already of our own - // Error type. - var asError *Error - if errors.As(err, &asError) { - asError.fatal = true // Set fatal even for existing Error types - return err - } - err2 := NewError(err) - err2.(*Error).fatal = true - return err2 -} diff --git a/errors/errors_test.go b/errors/errors_test.go deleted file mode 100644 index c3f2a71..0000000 --- a/errors/errors_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package errors - -import ( - "errors" - "fmt" - "testing" -) - -type CustomError struct { - Err error -} - -func (e *CustomError) Error() string { return e.Err.Error() } - -func IsCustomError(err error) bool { - var asError *CustomError - return errors.As(err, &asError) -} - -func TestWrapping(t *testing.T) { - t.Parallel() - originalErr := errors.New("original error") - err1 := NewError(originalErr) - if !errors.Is(err1, originalErr) { - t.Errorf("original error is not wrapped") - } - if !Is(err1, originalErr) { - t.Errorf("original error is not wrapped") - } - unwrappedErr := errors.Unwrap(err1) - if !errors.Is(unwrappedErr, originalErr) { - t.Errorf("original error is not wrapped") - } - if !Is(unwrappedErr, originalErr) { - t.Errorf("original error is not wrapped") - } - unwrappedErr = Unwrap(err1) - if !errors.Is(unwrappedErr, originalErr) { - t.Errorf("original error is not wrapped") - } - if !Is(unwrappedErr, originalErr) { - t.Errorf("original error is not wrapped") - } - wrappedErr := fmt.Errorf("wrapped: %w", originalErr) - if !errors.Is(wrappedErr, originalErr) { - t.Errorf("original error is not wrapped") - } - if !Is(wrappedErr, originalErr) { - t.Errorf("original error is not wrapped") - } -} - -func TestNewError(t *testing.T) { - t.Parallel() - originalErr := &CustomError{errors.New("err1")} - if !IsCustomError(originalErr) { - t.Errorf("TestNewError fail #1") - } - err1 := NewError(originalErr) - if !IsCustomError(err1) { - t.Errorf("TestNewError fail #2") - } - wrappedErr1 := fmt.Errorf("wrapped %w", err1) - if !IsCustomError(wrappedErr1) { - t.Errorf("TestNewError fail #3") - } - unwrappedErr1 := Unwrap(wrappedErr1) - if !IsCustomError(unwrappedErr1) { - t.Errorf("TestNewError fail #4") - } -} - -func TestIsFatal(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - err error - want bool - }{ - { - name: "nil error", - err: nil, - want: false, - }, - { - name: "simple non-fatal error", - err: fmt.Errorf("regular error"), - want: false, - }, - { - name: "direct fatal error", - err: NewFatalError(fmt.Errorf("fatal error")), - want: true, - }, - { - name: "non-fatal Error type", - err: NewError(fmt.Errorf("non-fatal error")), - want: false, - }, - { - name: "wrapped fatal error - one level", - err: fmt.Errorf("outer: %w", NewFatalError(fmt.Errorf("inner fatal"))), - want: true, - }, - { - name: "wrapped fatal error - two levels", - err: fmt.Errorf("outer: %w", - fmt.Errorf("middle: %w", - NewFatalError(fmt.Errorf("inner fatal")))), - want: true, - }, - { - name: "wrapped fatal error - three levels", - err: fmt.Errorf("outer: %w", - fmt.Errorf("middle1: %w", - fmt.Errorf("middle2: %w", - NewFatalError(fmt.Errorf("inner fatal"))))), - want: true, - }, - { - name: "multiple wrapped errors - non-fatal", - err: fmt.Errorf("outer: %w", - fmt.Errorf("middle: %w", - fmt.Errorf("inner: %w", - NewError(fmt.Errorf("base error"))))), - want: false, - }, - { - name: "wrapped non-fatal Error type", - err: fmt.Errorf("outer: %w", NewError(fmt.Errorf("inner"))), - want: false, - }, - { - name: "wrapped basic error", - err: fmt.Errorf("outer: %w", fmt.Errorf("inner")), - want: false, - }, - { - name: "fatal error wrapping fatal error", - err: NewFatalError(NewFatalError(fmt.Errorf("double fatal"))), - want: true, - }, - { - name: "fatal error wrapping non-fatal Error", - err: NewFatalError(NewError(fmt.Errorf("mixed"))), - want: true, - }, - { - name: "non-fatal Error wrapping fatal error", - err: NewError(NewFatalError(fmt.Errorf("mixed"))), - want: true, - }, - { - name: "Error wrapping Error", - err: NewError(NewError(fmt.Errorf("double wrapped"))), - want: false, - }, - { - name: "wrapped nil error", - err: fmt.Errorf("outer: %w", nil), - want: false, - }, - { - name: "fatal wrapping nil", - err: NewFatalError(nil), - want: false, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - got := IsFatal(tt.err) - if got != tt.want { - t.Errorf("IsFatal() = %v, want %v", got, tt.want) - if tt.err != nil { - t.Errorf("Error was: %v", tt.err) - } - } - }) - } -} diff --git a/gemini/errors.go b/gemini/errors.go index 2d60b3e..06f2756 100644 --- a/gemini/errors.go +++ b/gemini/errors.go @@ -3,7 +3,7 @@ package gemini import ( "fmt" - "gemini-grc/errors" + "github.com/antanst/go_errors" ) // GeminiError is used to represent @@ -48,5 +48,5 @@ func IsGeminiError(err error) bool { return false } var asError *GeminiError - return errors.As(err, &asError) + return go_errors.As(err, &asError) } diff --git a/gemini/geminiLinks.go b/gemini/geminiLinks.go index a3762a5..c77fd84 100644 --- a/gemini/geminiLinks.go +++ b/gemini/geminiLinks.go @@ -7,9 +7,9 @@ import ( "gemini-grc/common/linkList" url2 "gemini-grc/common/url" - "gemini-grc/errors" "gemini-grc/logging" "gemini-grc/util" + "github.com/antanst/go_errors" ) func GetPageLinks(currentURL url2.URL, gemtext string) linkList.LinkList { @@ -37,14 +37,14 @@ func ParseGeminiLinkLine(linkLine string, currentURL string) (*url2.URL, error) // Check: currentURL is parseable baseURL, err := url.Parse(currentURL) if err != nil { - return nil, errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) + return nil, go_errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) } // Extract the actual URL and the description re := regexp.MustCompile(`^=>[ \t]+(\S+)([ \t]+.*)?`) matches := re.FindStringSubmatch(linkLine) if len(matches) == 0 { - return nil, errors.NewError(fmt.Errorf("error parsing link line: no regexp match for line %s", linkLine)) + return nil, go_errors.NewError(fmt.Errorf("error parsing link line: no regexp match for line %s", linkLine)) } originalURLStr := matches[1] @@ -52,7 +52,7 @@ func ParseGeminiLinkLine(linkLine string, currentURL string) (*url2.URL, error) // Check: Unescape the URL if escaped _, err = url.QueryUnescape(originalURLStr) if err != nil { - return nil, errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) + return nil, go_errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) } description := "" @@ -63,7 +63,7 @@ func ParseGeminiLinkLine(linkLine string, currentURL string) (*url2.URL, error) // Parse the URL from the link line parsedURL, err := url.Parse(originalURLStr) if err != nil { - return nil, errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) + return nil, go_errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) } // If link URL is relative, resolve full URL @@ -80,7 +80,7 @@ func ParseGeminiLinkLine(linkLine string, currentURL string) (*url2.URL, error) finalURL, err := url2.ParseURL(parsedURL.String(), description, true) if err != nil { - return nil, errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) + return nil, go_errors.NewError(fmt.Errorf("error parsing link line: %w input '%s'", err, linkLine)) } return finalURL, nil diff --git a/gemini/network.go b/gemini/network.go index 79a0c38..b2cd280 100644 --- a/gemini/network.go +++ b/gemini/network.go @@ -16,8 +16,8 @@ import ( "gemini-grc/common/snapshot" _url "gemini-grc/common/url" "gemini-grc/config" - "gemini-grc/errors" "gemini-grc/logging" + "github.com/antanst/go_errors" "github.com/guregu/null/v5" ) @@ -41,8 +41,8 @@ func Visit(url string) (s *snapshot.Snapshot, err error) { err = nil } else if IsGeminiError(err) { s.Error = null.StringFrom(err.Error()) - s.Header = null.StringFrom(errors.Unwrap(err).(*GeminiError).Header) - s.ResponseCode = null.IntFrom(int64(errors.Unwrap(err).(*GeminiError).Code)) + s.Header = null.StringFrom(go_errors.Unwrap(err).(*GeminiError).Header) + s.ResponseCode = null.IntFrom(int64(go_errors.Unwrap(err).(*GeminiError).Code)) err = nil } else { s = nil @@ -73,7 +73,7 @@ func Visit(url string) (s *snapshot.Snapshot, err error) { func ConnectAndGetData(url string) ([]byte, error) { parsedURL, err := stdurl.Parse(url) if err != nil { - return nil, errors.NewError(err) + return nil, go_errors.NewError(err) } hostname := parsedURL.Hostname() port := parsedURL.Port() @@ -148,7 +148,7 @@ func ConnectAndGetData(url string) ([]byte, error) { return nil, errors2.NewHostError(err) } if err != nil { - if errors.Is(err, io.EOF) { + if go_errors.Is(err, io.EOF) { break } return nil, errors2.NewHostError(err) @@ -182,7 +182,7 @@ func processData(s snapshot.Snapshot, data []byte) (*snapshot.Snapshot, error) { if mimeType == "text/gemini" { validBody, err := BytesToValidUTF8(body) if err != nil { - return nil, errors.NewError(err) + return nil, go_errors.NewError(err) } s.GemText = null.StringFrom(validBody) } else { diff --git a/go.mod b/go.mod index a1b39df..2550fed 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module gemini-grc go 1.23.1 require ( + github.com/antanst/go_errors v0.0.1 github.com/guregu/null/v5 v5.0.0 github.com/jackc/pgx/v5 v5.7.2 github.com/jmoiron/sqlx v1.4.0 diff --git a/go.sum b/go.sum index d351f69..a2ea9c1 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/antanst/go_errors v0.0.1 h1:55BJ8I3u9IeLJxVslbI8Hv8fM0+fWyIE2VQXuwuYg9Y= +github.com/antanst/go_errors v0.0.1/go.mod h1:VDiDlRB7JfRhr6GMqdChBGT1XTBIfzELhg3Yq7sVwhM= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/gopher/errors.go b/gopher/errors.go index 2d59fef..75eddbe 100644 --- a/gopher/errors.go +++ b/gopher/errors.go @@ -1,8 +1,6 @@ package gopher -import ( - "gemini-grc/errors" -) +import "github.com/antanst/go_errors" // GopherError is an error encountered while // visiting a Gopher host, and is only for @@ -28,5 +26,5 @@ func IsGopherError(err error) bool { return false } var asError *GopherError - return errors.As(err, &asError) + return go_errors.As(err, &asError) } diff --git a/gopher/network.go b/gopher/network.go index 745c6e4..cd31a5f 100644 --- a/gopher/network.go +++ b/gopher/network.go @@ -15,8 +15,8 @@ import ( "gemini-grc/common/snapshot" _url "gemini-grc/common/url" "gemini-grc/config" - "gemini-grc/errors" "gemini-grc/logging" + "github.com/antanst/go_errors" "github.com/guregu/null/v5" ) @@ -118,7 +118,7 @@ func Visit(url string) (*snapshot.Snapshot, error) { func connectAndGetData(url string) ([]byte, error) { parsedURL, err := stdurl.Parse(url) if err != nil { - return nil, errors.NewError(err) + return nil, go_errors.NewError(err) } hostname := parsedURL.Hostname() @@ -169,7 +169,7 @@ func connectAndGetData(url string) ([]byte, error) { data = append(data, buf[:n]...) } if err != nil { - if errors.Is(err, io.EOF) { + if go_errors.Is(err, io.EOF) { break } return nil, errors2.NewHostError(err) diff --git a/main.go b/main.go index 62ea9a4..939ebf8 100644 --- a/main.go +++ b/main.go @@ -10,8 +10,8 @@ import ( "gemini-grc/common/blackList" "gemini-grc/config" "gemini-grc/db" - "gemini-grc/errors" "gemini-grc/logging" + "github.com/antanst/go_errors" "github.com/jmoiron/sqlx" "github.com/rs/zerolog" zlog "github.com/rs/zerolog/log" @@ -24,8 +24,8 @@ func main() { zlog.Logger = zlog.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: "[2006-01-02 15:04:05]"}) err := runApp() if err != nil { - var asErr *errors.Error - if errors.As(err, &asErr) { + var asErr *go_errors.Error + if go_errors.As(err, &asErr) { logging.LogError("Unexpected error: %v", err) _, _ = fmt.Fprintf(os.Stderr, "Unexpected error: %v", err) } else { @@ -73,7 +73,7 @@ func runApp() (err error) { logging.LogWarn("Received SIGINT or SIGTERM signal, exiting") return nil case err := <-common.ErrorsChan: - if errors.IsFatal(err) { + if go_errors.IsFatal(err) { return err } logging.LogError("%s", fmt.Sprintf("%v", err))