forked from Gitlink/gitlink-cli
116 lines
3.4 KiB
Go
116 lines
3.4 KiB
Go
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ErrorKind categorizes errors by user-actionability.
|
|
type ErrorKind string
|
|
|
|
const (
|
|
KindAuth ErrorKind = "auth" // Login/token issues — user can re-login
|
|
KindInput ErrorKind = "input" // Parameter issues — user can fix arguments
|
|
KindConfig ErrorKind = "config" // Config file issues — user can edit config
|
|
KindNetwork ErrorKind = "network" // Network issues — user can check/retry
|
|
KindGit ErrorKind = "git" // Git repo issues — user needs correct directory
|
|
KindServer ErrorKind = "server" // Server-side error — user should wait or contact admin
|
|
KindNotFound ErrorKind = "not_found" // Resource not found — user can check ID
|
|
KindForbidden ErrorKind = "forbidden" // Permission denied — user can request access
|
|
KindUnknown ErrorKind = "unknown" // Unclassified error
|
|
)
|
|
|
|
// CLIError is the unified CLI error type with multi-layered information.
|
|
type CLIError struct {
|
|
Kind ErrorKind // Error category for programmatic handling
|
|
Message string // Human-readable description of what went wrong
|
|
Detail string // Low-level technical detail (shown in debug mode)
|
|
Suggestion string // Actionable advice for the user
|
|
Command string // The command that triggered the error (e.g., "issue +create")
|
|
Cause error // The underlying error
|
|
}
|
|
|
|
func (e *CLIError) Error() string {
|
|
var b strings.Builder
|
|
|
|
// Header line: kind + command
|
|
b.WriteString(string(e.Kind))
|
|
b.WriteString(" error")
|
|
if e.Command != "" {
|
|
b.WriteString(" — ")
|
|
b.WriteString(e.Command)
|
|
}
|
|
|
|
// Body: message
|
|
if e.Message != "" {
|
|
b.WriteString("\n\n reason: ")
|
|
b.WriteString(e.Message)
|
|
}
|
|
|
|
// Suggestion
|
|
if e.Suggestion != "" {
|
|
b.WriteString("\n suggestion: ")
|
|
b.WriteString(e.Suggestion)
|
|
}
|
|
|
|
// Detail (always included in Error() so users see the raw cause)
|
|
if e.Detail != "" {
|
|
b.WriteString("\n detail: ")
|
|
b.WriteString(e.Detail)
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
func (e *CLIError) Unwrap() error {
|
|
return e.Cause
|
|
}
|
|
|
|
// New creates a CLIError with the given parameters.
|
|
func New(kind ErrorKind, message, suggestion string) *CLIError {
|
|
return &CLIError{
|
|
Kind: kind,
|
|
Message: message,
|
|
Suggestion: suggestion,
|
|
}
|
|
}
|
|
|
|
// Wrap creates a CLIError that wraps an underlying cause.
|
|
func Wrap(kind ErrorKind, message, suggestion string, cause error) *CLIError {
|
|
return &CLIError{
|
|
Kind: kind,
|
|
Message: message,
|
|
Suggestion: suggestion,
|
|
Cause: cause,
|
|
Detail: cause.Error(),
|
|
}
|
|
}
|
|
|
|
// WithCommand sets the command context on the error.
|
|
func (e *CLIError) WithCommand(cmd string) *CLIError {
|
|
e.Command = cmd
|
|
return e
|
|
}
|
|
|
|
// InputError is a convenience constructor for parameter errors.
|
|
func InputError(message, suggestion string) *CLIError {
|
|
return New(KindInput, message, suggestion)
|
|
}
|
|
|
|
// AuthError is a convenience constructor for authentication errors.
|
|
func AuthError(message, suggestion string) *CLIError {
|
|
return New(KindAuth, message, suggestion)
|
|
}
|
|
|
|
// ConfigError creates a config-related error with the config file path in the suggestion.
|
|
func ConfigError(message string, cause error) *CLIError {
|
|
return Wrap(KindConfig, message,
|
|
fmt.Sprintf("检查配置文件 %s 是否正确", configPathPlaceholder()), cause)
|
|
}
|
|
|
|
// configPathPlaceholder avoids circular import; the actual path will be resolved
|
|
// in output formatting.
|
|
func configPathPlaceholder() string {
|
|
return "~/.config/gitlink-cli/config.yaml"
|
|
}
|