Mining/pkg/mining/errors.go
Claude 7cf7e13b51
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
ax(mining): replace prose comments in errors.go with usage examples
AX principle 2: comments must show HOW with real values, not restate
what the function name already says. All 14 error constructor comments
converted from "ErrX creates a Y error" to concrete call-site examples.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 08:01:02 +01:00

239 lines
7.1 KiB
Go

package mining
import (
"fmt"
"net/http"
)
// Error codes for the mining package
const (
ErrCodeMinerNotFound = "MINER_NOT_FOUND"
ErrCodeMinerExists = "MINER_EXISTS"
ErrCodeMinerNotRunning = "MINER_NOT_RUNNING"
ErrCodeInstallFailed = "INSTALL_FAILED"
ErrCodeStartFailed = "START_FAILED"
ErrCodeStopFailed = "STOP_FAILED"
ErrCodeInvalidConfig = "INVALID_CONFIG"
ErrCodeInvalidInput = "INVALID_INPUT"
ErrCodeUnsupportedMiner = "UNSUPPORTED_MINER"
ErrCodeNotSupported = "NOT_SUPPORTED"
ErrCodeConnectionFailed = "CONNECTION_FAILED"
ErrCodeServiceUnavailable = "SERVICE_UNAVAILABLE"
ErrCodeTimeout = "TIMEOUT"
ErrCodeDatabaseError = "DATABASE_ERROR"
ErrCodeProfileNotFound = "PROFILE_NOT_FOUND"
ErrCodeProfileExists = "PROFILE_EXISTS"
ErrCodeInternalError = "INTERNAL_ERROR"
ErrCodeInternal = "INTERNAL_ERROR" // Alias for consistency
)
// MiningError is a structured error type for the mining package
type MiningError struct {
Code string // Machine-readable error code
Message string // Human-readable message
Details string // Technical details (for debugging)
Suggestion string // What to do next
Retryable bool // Can the client retry?
HTTPStatus int // HTTP status code to return
Cause error // Underlying error
}
func (e *MiningError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("%s: %s (%v)", e.Code, e.Message, e.Cause)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func (e *MiningError) Unwrap() error {
return e.Cause
}
func (e *MiningError) WithCause(err error) *MiningError {
e.Cause = err
return e
}
func (e *MiningError) WithDetails(details string) *MiningError {
e.Details = details
return e
}
func (e *MiningError) WithSuggestion(suggestion string) *MiningError {
e.Suggestion = suggestion
return e
}
func (e *MiningError) IsRetryable() bool {
return e.Retryable
}
func (e *MiningError) StatusCode() int {
if e.HTTPStatus == 0 {
return http.StatusInternalServerError
}
return e.HTTPStatus
}
// NewMiningError("START_FAILED", "failed to start xmrig").WithCause(err)
func NewMiningError(code, message string) *MiningError {
return &MiningError{
Code: code,
Message: message,
HTTPStatus: http.StatusInternalServerError,
}
}
// return ErrMinerNotFound("xmrig").WithCause(err)
func ErrMinerNotFound(name string) *MiningError {
return &MiningError{
Code: ErrCodeMinerNotFound,
Message: fmt.Sprintf("miner '%s' not found", name),
Suggestion: "Check that the miner name is correct and that it is running",
Retryable: false,
HTTPStatus: http.StatusNotFound,
}
}
// return ErrMinerExists("xmrig")
func ErrMinerExists(name string) *MiningError {
return &MiningError{
Code: ErrCodeMinerExists,
Message: fmt.Sprintf("miner '%s' is already running", name),
Suggestion: "Stop the existing miner first or use a different configuration",
Retryable: false,
HTTPStatus: http.StatusConflict,
}
}
// return ErrMinerNotRunning("xmrig")
func ErrMinerNotRunning(name string) *MiningError {
return &MiningError{
Code: ErrCodeMinerNotRunning,
Message: fmt.Sprintf("miner '%s' is not running", name),
Suggestion: "Start the miner first before performing this operation",
Retryable: false,
HTTPStatus: http.StatusBadRequest,
}
}
// return ErrInstallFailed("xmrig").WithCause(err)
func ErrInstallFailed(minerType string) *MiningError {
return &MiningError{
Code: ErrCodeInstallFailed,
Message: fmt.Sprintf("failed to install %s", minerType),
Suggestion: "Check your internet connection and try again",
Retryable: true,
HTTPStatus: http.StatusInternalServerError,
}
}
// return ErrStartFailed("xmrig").WithCause(err)
func ErrStartFailed(name string) *MiningError {
return &MiningError{
Code: ErrCodeStartFailed,
Message: fmt.Sprintf("failed to start miner '%s'", name),
Suggestion: "Check the miner configuration and logs for details",
Retryable: true,
HTTPStatus: http.StatusInternalServerError,
}
}
// return ErrStopFailed("xmrig").WithCause(err)
func ErrStopFailed(name string) *MiningError {
return &MiningError{
Code: ErrCodeStopFailed,
Message: fmt.Sprintf("failed to stop miner '%s'", name),
Suggestion: "The miner process may need to be terminated manually",
Retryable: true,
HTTPStatus: http.StatusInternalServerError,
}
}
// return ErrInvalidConfig("wallet address is required")
func ErrInvalidConfig(reason string) *MiningError {
return &MiningError{
Code: ErrCodeInvalidConfig,
Message: fmt.Sprintf("invalid configuration: %s", reason),
Suggestion: "Review the configuration and ensure all required fields are provided",
Retryable: false,
HTTPStatus: http.StatusBadRequest,
}
}
// return ErrUnsupportedMiner("nicehash")
func ErrUnsupportedMiner(minerType string) *MiningError {
return &MiningError{
Code: ErrCodeUnsupportedMiner,
Message: fmt.Sprintf("unsupported miner type: %s", minerType),
Suggestion: "Use one of the supported miner types: xmrig, tt-miner",
Retryable: false,
HTTPStatus: http.StatusBadRequest,
}
}
// return ErrConnectionFailed("pool.minexmr.com:4444").WithCause(err)
func ErrConnectionFailed(target string) *MiningError {
return &MiningError{
Code: ErrCodeConnectionFailed,
Message: fmt.Sprintf("failed to connect to %s", target),
Suggestion: "Check network connectivity and try again",
Retryable: true,
HTTPStatus: http.StatusServiceUnavailable,
}
}
// return ErrTimeout("stats poll")
func ErrTimeout(operation string) *MiningError {
return &MiningError{
Code: ErrCodeTimeout,
Message: fmt.Sprintf("operation timed out: %s", operation),
Suggestion: "The operation is taking longer than expected, try again later",
Retryable: true,
HTTPStatus: http.StatusGatewayTimeout,
}
}
// return ErrDatabaseError("save profile")
func ErrDatabaseError(operation string) *MiningError {
return &MiningError{
Code: ErrCodeDatabaseError,
Message: fmt.Sprintf("database error during %s", operation),
Suggestion: "This may be a temporary issue, try again",
Retryable: true,
HTTPStatus: http.StatusInternalServerError,
}
}
// return ErrProfileNotFound("default-xmr")
func ErrProfileNotFound(id string) *MiningError {
return &MiningError{
Code: ErrCodeProfileNotFound,
Message: fmt.Sprintf("profile '%s' not found", id),
Suggestion: "Check that the profile ID is correct",
Retryable: false,
HTTPStatus: http.StatusNotFound,
}
}
// return ErrProfileExists("default-xmr")
func ErrProfileExists(name string) *MiningError {
return &MiningError{
Code: ErrCodeProfileExists,
Message: fmt.Sprintf("profile '%s' already exists", name),
Suggestion: "Use a different name or update the existing profile",
Retryable: false,
HTTPStatus: http.StatusConflict,
}
}
// return ErrInternal("unexpected nil response from stats API")
func ErrInternal(message string) *MiningError {
return &MiningError{
Code: ErrCodeInternalError,
Message: message,
Suggestion: "Please report this issue if it persists",
Retryable: true,
HTTPStatus: http.StatusInternalServerError,
}
}