6fd25e4d02
- Add AppError with error codes and hints system - Implement ConnectionError for SSH-specific errors - Add HandleSSHError for intelligent SSH error parsing - Add FormatConnectionError for user-friendly output - All 5 test functions passing (TestAppError, TestConnectionError, TestHandleSSHError, TestHandleSSHErrorNil, TestFormatConnectionError) - Update PROJECT_STATE.md to reflect Task 4 completion Progress: Tasks 1-4 complete (~30%)
105 lines
3.0 KiB
Go
105 lines
3.0 KiB
Go
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ConnectionError represents SSH connection errors with helpful hints
|
|
type ConnectionError struct {
|
|
Type string // "auth", "network", "timeout", "config", "unknown"
|
|
Message string
|
|
Details string
|
|
Hints []string
|
|
}
|
|
|
|
// Error implements the error interface
|
|
func (e *ConnectionError) Error() string {
|
|
return fmt.Sprintf("Connection Error (%s): %s\nDetails: %s", e.Type, e.Message, e.Details)
|
|
}
|
|
|
|
// NewConnectionError creates a new ConnectionError
|
|
func NewConnectionError(errType, message, details string, hints []string) *ConnectionError {
|
|
return &ConnectionError{
|
|
Type: errType,
|
|
Message: message,
|
|
Details: details,
|
|
Hints: hints,
|
|
}
|
|
}
|
|
|
|
// HandleSSHError processes SSH errors and returns user-friendly errors
|
|
func HandleSSHError(err error) *ConnectionError {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
errStr := err.Error()
|
|
|
|
switch {
|
|
case strings.Contains(errStr, "connection refused"):
|
|
return &ConnectionError{
|
|
Type: "network",
|
|
Message: "Cannot connect to server",
|
|
Details: errStr,
|
|
Hints: []string{"Check if server is running", "Verify firewall rules", "Confirm hostname and port"},
|
|
}
|
|
|
|
case strings.Contains(errStr, "authentication failed"), strings.Contains(errStr, "unable to authenticate"):
|
|
return &ConnectionError{
|
|
Type: "auth",
|
|
Message: "Authentication failed",
|
|
Details: errStr,
|
|
Hints: []string{"Verify username and password", "Check SSH key is loaded", "Test with native SSH client"},
|
|
}
|
|
|
|
case strings.Contains(errStr, "timeout"), strings.Contains(errStr, "timed out"):
|
|
return &ConnectionError{
|
|
Type: "timeout",
|
|
Message: "Connection timeout",
|
|
Details: errStr,
|
|
Hints: []string{"Check network connectivity", "Try increasing timeout", "Verify server is reachable"},
|
|
}
|
|
|
|
case strings.Contains(errStr, "no such host"), strings.Contains(errStr, "hostname"):
|
|
return &ConnectionError{
|
|
Type: "config",
|
|
Message: "Invalid hostname",
|
|
Details: errStr,
|
|
Hints: []string{"Verify hostname spelling", "Check DNS resolution", "Try IP address instead"},
|
|
}
|
|
|
|
case strings.Contains(errStr, "permission denied"):
|
|
return &ConnectionError{
|
|
Type: "auth",
|
|
Message: "Permission denied",
|
|
Details: errStr,
|
|
Hints: []string{"Check user permissions on server", "Verify account is not locked", "Check authentication method"},
|
|
}
|
|
|
|
default:
|
|
return &ConnectionError{
|
|
Type: "unknown",
|
|
Message: "Connection failed",
|
|
Details: errStr,
|
|
Hints: []string{"Check host configuration", "Verify network settings", "Test with standard SSH client"},
|
|
}
|
|
}
|
|
}
|
|
|
|
// FormatConnectionError formats connection error for user-friendly display
|
|
func FormatConnectionError(err *ConnectionError) string {
|
|
var output strings.Builder
|
|
|
|
output.WriteString(fmt.Sprintf("❌ Connection Error: %s\n\n", err.Message))
|
|
output.WriteString(fmt.Sprintf("Details: %s\n\n", err.Details))
|
|
|
|
if len(err.Hints) > 0 {
|
|
output.WriteString("Possible solutions:\n")
|
|
for i, hint := range err.Hints {
|
|
output.WriteString(fmt.Sprintf(" %d. %s\n", i+1, hint))
|
|
}
|
|
}
|
|
|
|
return output.String()
|
|
} |