feat: initial goget release — modern IPv6-first download utility
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
//go:build linux || freebsd
|
||||
// +build linux freebsd
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// ErrorType defines the error type
|
||||
type ErrorType string
|
||||
|
||||
const (
|
||||
ErrNetwork ErrorType = "NETWORK"
|
||||
ErrProtocol ErrorType = "PROTOCOL"
|
||||
ErrFile ErrorType = "FILE"
|
||||
ErrTimeout ErrorType = "TIMEOUT"
|
||||
ErrAuth ErrorType = "AUTH"
|
||||
ErrChecksum ErrorType = "CHECKSUM"
|
||||
ErrUnsupported ErrorType = "UNSUPPORTED"
|
||||
ErrCancelled ErrorType = "CANCELLED"
|
||||
)
|
||||
|
||||
// GogetError is a structured error with context
|
||||
type GogetError struct {
|
||||
Type ErrorType
|
||||
Message string
|
||||
Cause error
|
||||
URL string
|
||||
Hint string // Actionable suggestion for the user
|
||||
Details map[string]string
|
||||
}
|
||||
|
||||
func (e *GogetError) Error() string {
|
||||
var msg string
|
||||
if e.Cause != nil {
|
||||
msg = fmt.Sprintf("[%s] %s: %v", e.Type, e.Message, e.Cause)
|
||||
} else {
|
||||
msg = fmt.Sprintf("[%s] %s", e.Type, e.Message)
|
||||
}
|
||||
if e.Hint != "" {
|
||||
msg += fmt.Sprintf(" (hint: %s)", e.Hint)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func (e *GogetError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
// GetDetail returns a value from the Details map, or an empty string if the key is absent.
|
||||
func (e *GogetError) GetDetail(key string) string {
|
||||
if e.Details == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Details[key]
|
||||
}
|
||||
|
||||
// NewNetworkError creates a network error
|
||||
func NewNetworkError(msg string, cause error, url string) *GogetError {
|
||||
return &GogetError{
|
||||
Type: ErrNetwork,
|
||||
Message: msg,
|
||||
Cause: cause,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
|
||||
// NewProtocolError creates a protocol error
|
||||
func NewProtocolError(msg string, cause error, url string) *GogetError {
|
||||
return &GogetError{
|
||||
Type: ErrProtocol,
|
||||
Message: msg,
|
||||
Cause: cause,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileError creates a file error
|
||||
func NewFileError(msg string, cause error) *GogetError {
|
||||
return &GogetError{
|
||||
Type: ErrFile,
|
||||
Message: msg,
|
||||
Cause: cause,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTimeoutError creates a timeout error
|
||||
func NewTimeoutError(msg string, url string) *GogetError {
|
||||
return &GogetError{
|
||||
Type: ErrTimeout,
|
||||
Message: msg,
|
||||
URL: url,
|
||||
Hint: "increase --max-time or --connect-timeout, or check network connectivity",
|
||||
}
|
||||
}
|
||||
|
||||
// NewChecksumError creates a checksum error
|
||||
func NewChecksumError(expected, actual string) *GogetError {
|
||||
return &GogetError{
|
||||
Type: ErrChecksum,
|
||||
Message: fmt.Sprintf("checksum mismatch: expected %s, got %s", expected, actual),
|
||||
Hint: "verify the file integrity with an external checksum tool or re-download",
|
||||
Details: map[string]string{
|
||||
"expected": expected,
|
||||
"actual": actual,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnsupportedError creates an unsupported operation error
|
||||
func NewUnsupportedError(msg string) *GogetError {
|
||||
return &GogetError{
|
||||
Type: ErrUnsupported,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// WithHint returns the error with the hint set. Use for adding contextual,
|
||||
// actionable suggestions to an existing error without creating a new one.
|
||||
func (e *GogetError) WithHint(hint string) *GogetError {
|
||||
e.Hint = hint
|
||||
return e
|
||||
}
|
||||
|
||||
// SafeURL returns the URL string with credentials removed for safe error logging.
|
||||
func SafeURL(u *url.URL) string {
|
||||
if u == nil {
|
||||
return ""
|
||||
}
|
||||
clean := *u
|
||||
clean.User = nil
|
||||
return clean.String()
|
||||
}
|
||||
Reference in New Issue
Block a user