// Package ucxl_codes provides standardized UCXL error and response codes // for consistent cross-service communication and client handling. // // Based on UCXL-ERROR-CODES.md and UCXL-RESPONSE-CODES.md v1.0 package ucxl_codes import ( "encoding/json" "fmt" "time" "github.com/google/uuid" ) // UCXLErrorCode represents standard UCXL error codes following format: UCXL-- type UCXLErrorCode string const ( // 400 - Client Errors ErrorInvalidAddress UCXLErrorCode = "UCXL-400-INVALID_ADDRESS" ErrorMissingField UCXLErrorCode = "UCXL-400-MISSING_FIELD" ErrorInvalidFormat UCXLErrorCode = "UCXL-400-INVALID_FORMAT" // 401 - Authentication ErrorUnauthorized UCXLErrorCode = "UCXL-401-UNAUTHORIZED" // 403 - Authorization ErrorForbidden UCXLErrorCode = "UCXL-403-FORBIDDEN" // 404 - Not Found ErrorNotFound UCXLErrorCode = "UCXL-404-NOT_FOUND" // 409 - Conflict ErrorConflict UCXLErrorCode = "UCXL-409-CONFLICT" // 422 - Unprocessable Entity ErrorUnprocessableEntity UCXLErrorCode = "UCXL-422-UNPROCESSABLE_ENTITY" // 429 - Rate Limiting ErrorRateLimit UCXLErrorCode = "UCXL-429-RATE_LIMIT" // 500 - Server Errors ErrorInternalError UCXLErrorCode = "UCXL-500-INTERNAL_ERROR" // 503 - Service Unavailable ErrorServiceUnavailable UCXLErrorCode = "UCXL-503-SERVICE_UNAVAILABLE" // 504 - Gateway Timeout ErrorGatewayTimeout UCXLErrorCode = "UCXL-504-GATEWAY_TIMEOUT" ) // UCXLResponseCode represents standard UCXL response codes following format: UCXL-- type UCXLResponseCode string const ( // 200 - Success ResponseOK UCXLResponseCode = "UCXL-200-OK" // 201 - Created ResponseCreated UCXLResponseCode = "UCXL-201-CREATED" // 202 - Accepted (Async) ResponseAccepted UCXLResponseCode = "UCXL-202-ACCEPTED" // 204 - No Content ResponseNoContent UCXLResponseCode = "UCXL-204-NO_CONTENT" // 206 - Partial Content ResponsePartialContent UCXLResponseCode = "UCXL-206-PARTIAL_CONTENT" // 304 - Not Modified (Caching) ResponseNotModified UCXLResponseCode = "UCXL-304-NOT_MODIFIED" ) // HTTPStatus returns the HTTP status code for the error code func (e UCXLErrorCode) HTTPStatus() int { switch e { case ErrorInvalidAddress, ErrorMissingField, ErrorInvalidFormat: return 400 case ErrorUnauthorized: return 401 case ErrorForbidden: return 403 case ErrorNotFound: return 404 case ErrorConflict: return 409 case ErrorUnprocessableEntity: return 422 case ErrorRateLimit: return 429 case ErrorInternalError: return 500 case ErrorServiceUnavailable: return 503 case ErrorGatewayTimeout: return 504 default: return 500 } } // DefaultMessage returns the default error message for this code func (e UCXLErrorCode) DefaultMessage() string { switch e { case ErrorInvalidAddress: return "Invalid UCXL address format" case ErrorMissingField: return "Required field is missing" case ErrorInvalidFormat: return "Input does not match the expected format" case ErrorUnauthorized: return "Authentication credentials missing or invalid" case ErrorForbidden: return "Insufficient permissions for the action" case ErrorNotFound: return "Requested resource not found" case ErrorConflict: return "Conflict with current state" case ErrorUnprocessableEntity: return "Semantic validation failed" case ErrorRateLimit: return "Too many requests; rate limiting in effect" case ErrorInternalError: return "Internal server error" case ErrorServiceUnavailable: return "Service is currently unavailable" case ErrorGatewayTimeout: return "Downstream gateway timed out" default: return "Unknown error" } } // IsClientError checks if this is a client error (4xx) func (e UCXLErrorCode) IsClientError() bool { status := e.HTTPStatus() return status >= 400 && status < 500 } // IsServerError checks if this is a server error (5xx) func (e UCXLErrorCode) IsServerError() bool { status := e.HTTPStatus() return status >= 500 && status < 600 } // ShouldRetry checks if this error should trigger a retry func (e UCXLErrorCode) ShouldRetry() bool { switch e { case ErrorRateLimit, ErrorInternalError, ErrorServiceUnavailable, ErrorGatewayTimeout: return true default: return false } } // HTTPStatus returns the HTTP status code for the response code func (r UCXLResponseCode) HTTPStatus() int { switch r { case ResponseOK: return 200 case ResponseCreated: return 201 case ResponseAccepted: return 202 case ResponseNoContent: return 204 case ResponsePartialContent: return 206 case ResponseNotModified: return 304 default: return 200 } } // DefaultMessage returns the default success message for this code func (r UCXLResponseCode) DefaultMessage() string { switch r { case ResponseOK: return "Request completed successfully" case ResponseCreated: return "Resource created successfully" case ResponseAccepted: return "Request accepted for processing" case ResponseNoContent: return "Request completed with no content to return" case ResponsePartialContent: return "Partial results returned" case ResponseNotModified: return "Resource not modified since last fetch" default: return "Success" } } // IsAsync checks if this indicates an asynchronous operation func (r UCXLResponseCode) IsAsync() bool { return r == ResponseAccepted } // IsPartial checks if this indicates partial/incomplete results func (r UCXLResponseCode) IsPartial() bool { return r == ResponsePartialContent || r == ResponseAccepted } // UCXLErrorResponse represents the standardized UCXL error response payload type UCXLErrorResponse struct { Error UCXLError `json:"error"` } // UCXLError contains the error details type UCXLError struct { Code UCXLErrorCode `json:"code"` Message string `json:"message"` Details map[string]interface{} `json:"details,omitempty"` Source string `json:"source"` Path string `json:"path"` RequestID string `json:"request_id"` Timestamp time.Time `json:"timestamp"` Cause string `json:"cause,omitempty"` } // UCXLSuccessResponse represents the standardized UCXL success response payload type UCXLSuccessResponse struct { Response UCXLResponse `json:"response"` } // UCXLResponse contains the response details type UCXLResponse struct { Code UCXLResponseCode `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` Details map[string]interface{} `json:"details,omitempty"` RequestID string `json:"request_id"` Timestamp time.Time `json:"timestamp"` } // ErrorBuilder helps build standardized error responses type ErrorBuilder struct { code UCXLErrorCode message string details map[string]interface{} source string path string requestID string cause string } // NewErrorBuilder creates a new error builder with the specified code func NewErrorBuilder(code UCXLErrorCode) *ErrorBuilder { return &ErrorBuilder{ code: code, details: make(map[string]interface{}), source: "ucxl-api/v1", path: "/", requestID: fmt.Sprintf("req-%s", uuid.New().String()[:8]), } } // Message sets a custom error message func (b *ErrorBuilder) Message(message string) *ErrorBuilder { b.message = message return b } // Field adds details about the field that failed func (b *ErrorBuilder) Field(field string, provided interface{}) *ErrorBuilder { b.details["field"] = field b.details["provided"] = provided return b } // ExpectedFormat adds expected format information func (b *ErrorBuilder) ExpectedFormat(format string) *ErrorBuilder { b.details["expected_format"] = format return b } // Detail adds a detail field func (b *ErrorBuilder) Detail(key string, value interface{}) *ErrorBuilder { b.details[key] = value return b } // Source sets the source service func (b *ErrorBuilder) Source(source string) *ErrorBuilder { b.source = source return b } // Path sets the request path func (b *ErrorBuilder) Path(path string) *ErrorBuilder { b.path = path return b } // RequestID sets the request ID func (b *ErrorBuilder) RequestID(requestID string) *ErrorBuilder { b.requestID = requestID return b } // Cause sets the error cause func (b *ErrorBuilder) Cause(cause string) *ErrorBuilder { b.cause = cause return b } // Build creates the error response func (b *ErrorBuilder) Build() *UCXLErrorResponse { message := b.message if message == "" { message = b.code.DefaultMessage() } var details map[string]interface{} if len(b.details) > 0 { details = b.details } return &UCXLErrorResponse{ Error: UCXLError{ Code: b.code, Message: message, Details: details, Source: b.source, Path: b.path, RequestID: b.requestID, Timestamp: time.Now().UTC(), Cause: b.cause, }, } } // ResponseBuilder helps build standardized success responses type ResponseBuilder struct { code UCXLResponseCode message string data interface{} details map[string]interface{} requestID string } // NewResponseBuilder creates a new response builder with the specified code func NewResponseBuilder(code UCXLResponseCode) *ResponseBuilder { return &ResponseBuilder{ code: code, details: make(map[string]interface{}), requestID: fmt.Sprintf("req-%s", uuid.New().String()[:8]), } } // Message sets a custom success message func (b *ResponseBuilder) Message(message string) *ResponseBuilder { b.message = message return b } // Data sets the response data func (b *ResponseBuilder) Data(data interface{}) *ResponseBuilder { b.data = data return b } // Detail adds a detail field func (b *ResponseBuilder) Detail(key string, value interface{}) *ResponseBuilder { b.details[key] = value return b } // RequestID sets the request ID func (b *ResponseBuilder) RequestID(requestID string) *ResponseBuilder { b.requestID = requestID return b } // Build creates the success response func (b *ResponseBuilder) Build() *UCXLSuccessResponse { message := b.message if message == "" { message = b.code.DefaultMessage() } var details map[string]interface{} if len(b.details) > 0 { details = b.details } return &UCXLSuccessResponse{ Response: UCXLResponse{ Code: b.code, Message: message, Data: b.data, Details: details, RequestID: b.requestID, Timestamp: time.Now().UTC(), }, } } // JSON marshaling helpers // ToJSON converts the error response to JSON func (e *UCXLErrorResponse) ToJSON() ([]byte, error) { return json.Marshal(e) } // FromJSON creates an error response from JSON func (e *UCXLErrorResponse) FromJSON(data []byte) error { return json.Unmarshal(data, e) } // ToJSON converts the success response to JSON func (r *UCXLSuccessResponse) ToJSON() ([]byte, error) { return json.Marshal(r) } // FromJSON creates a success response from JSON func (r *UCXLSuccessResponse) FromJSON(data []byte) error { return json.Unmarshal(data, r) }