package cmd import ( "encoding/json" "errors" "os" "codeberg.org/romaintb/fgj/internal/api" ) // Error codes for structured error output. const ( ErrAuthRequired = "auth_required" ErrNotFound = "not_found" ErrAPIError = "api_error" ErrInvalidInput = "invalid_input" ErrGitDetectionFailed = "git_detection_failed" ErrNetworkError = "network_error" ) // CLIError is a structured error type for machine-readable output. type CLIError struct { Code string `json:"code"` Message string `json:"message"` Detail string `json:"detail,omitempty"` Status int `json:"status,omitempty"` } func (e *CLIError) Error() string { return e.Message } // NewCLIError creates a new CLIError with the given code and message. func NewCLIError(code, message string) *CLIError { return &CLIError{Code: code, Message: message} } // NewAPIError creates a CLIError from an HTTP status and message. func NewAPIError(status int, message string) *CLIError { return &CLIError{Code: ErrAPIError, Message: message, Status: status} } // writeJSONError writes a structured JSON error to stderr. // It attempts to extract structured info from known error types. // WriteJSONError writes a structured JSON error to stderr. // It is exported for use from main.go. func WriteJSONError(err error) { cliErr := &CLIError{ Code: ErrAPIError, Message: err.Error(), } // Try to extract structured info from the error chain. var apiErr *api.APIError var cErr *CLIError switch { case errors.As(err, &cErr): cliErr = cErr case errors.As(err, &apiErr): cliErr.Status = apiErr.StatusCode cliErr.Detail = apiErr.Body switch { case apiErr.StatusCode == 401 || apiErr.StatusCode == 403: cliErr.Code = ErrAuthRequired case apiErr.StatusCode == 404: cliErr.Code = ErrNotFound default: cliErr.Code = ErrAPIError } } enc := json.NewEncoder(os.Stderr) enc.SetIndent("", " ") _ = enc.Encode(cliErr) }