diff options
| author | Dan Engelbrecht <[email protected]> | 2025-09-05 13:02:27 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-09-05 13:02:27 +0200 |
| commit | 45b0307d42b22e04cee63467a8fdb898a2d8d552 (patch) | |
| tree | 905b3eb62af89269be5b15f4c407d900ce86f7f3 /src/zen/zen.cpp | |
| parent | Avoid mutating executable paths when copying files during full service instal... (diff) | |
| download | archived-zen-45b0307d42b22e04cee63467a8fdb898a2d8d552.tar.xz archived-zen-45b0307d42b22e04cee63467a8fdb898a2d8d552.zip | |
refactor zen command return value handling (#487)
- Improvement: Use consistent language for command line argument parsing errors
- Improvement: Changed zen command parsing errors to output help first and error last to make it easier to spot the error
- Improvement: Refactor zen command return codes to conform to valid Linux range (0-255)
kSuccess = 0,
kOtherError = 1,
kBadInput = 2,
kOutOfMemory = 16,
kOutOfDisk = 17,
kAssertError = 70,
kHttpOtherClientError = 80,
kHttpCantConnectError = 81,
kHttpNotFound = 66, // NotFound(404)
kHttpUnauthorized = 77, // Unauthorized(401),
kHttpSLLError = 82,
kHttpForbidden = 83, // Forbidden(403)
kHttpTimeout = 84, // RequestTimeout(408)
kHttpConflict = 85, // Conflict(409)
kHttpNoHost = 86,
kHttpOtherServerError = 90,
kHttpInternalServerError = 91, // InternalServerError(500)
kHttpServiceUnavailable = 69, // ServiceUnavailable(503)
kHttpBadGateway = 92, // BadGateway(502)
kHttpGatewayTimeout = 93, // GatewayTimeout(504)
Diffstat (limited to 'src/zen/zen.cpp')
| -rw-r--r-- | src/zen/zen.cpp | 204 |
1 files changed, 130 insertions, 74 deletions
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 240ba2a81..db63624ce 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -72,6 +72,35 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { +enum class ReturnCode : std::int8_t +{ + kSuccess = 0, + + kOtherError = 1, + + kBadInput = 2, + kOutOfMemory = 16, + kOutOfDisk = 17, + kAssertError = 70, + + kHttpOtherClientError = 80, + kHttpCantConnectError = 81, // CONNECTION_FAILURE + kHttpNotFound = 66, // NotFound(404) + kHttpUnauthorized = 77, // Unauthorized(401), + kHttpSLLError = + 82, // SSL_CONNECT_ERROR, SSL_LOCAL_CERTIFICATE_ERROR, SSL_REMOTE_CERTIFICATE_ERROR, SSL_CACERT_ERROR, GENERIC_SSL_ERROR + kHttpForbidden = 83, // Forbidden(403) + kHttpTimeout = 84, // NETWORK_RECEIVE_ERROR, NETWORK_SEND_FAILURE, OPERATION_TIMEDOUT, RequestTimeout(408) + kHttpConflict = 85, // Conflict(409) + kHttpNoHost = 86, // HOST_RESOLUTION_FAILURE, PROXY_RESOLUTION_FAILURE + + kHttpOtherServerError = 90, + kHttpInternalServerError = 91, // InternalServerError(500) + kHttpServiceUnavailable = 69, // ServiceUnavailable(503) + kHttpBadGateway = 92, // BadGateway(502) + kHttpGatewayTimeout = 93, // GatewayTimeout(504) +}; + ZenCmdCategory DefaultCategory{.Name = "general commands"}; ZenCmdCategory g_UtilitiesCategory{.Name = "utility commands"}; ZenCmdCategory g_ProjectStoreCategory{.Name = "project store commands"}; @@ -101,7 +130,7 @@ ZenCmdBase::ParseOptions(cxxopts::Options& CmdOptions, int argc, char** argv) } catch (const std::exception& Ex) { - throw zen::OptionParseException(Ex.what()); + throw zen::OptionParseException(Ex.what(), CmdOptions.help()); } CmdOptions.show_positional_help(); @@ -127,7 +156,7 @@ ZenCmdBase::ParseOptions(cxxopts::Options& CmdOptions, int argc, char** argv) First = false; } - throw zen::OptionParseException(fmt::format("Invalid arguments: {}", StringBuilder.ToView())); + throw zen::OptionParseException(fmt::format("Invalid arguments: {}", StringBuilder.ToView()), {}); } return true; @@ -161,62 +190,68 @@ ZenCmdBase::GetSubCommand(cxxopts::Options&, return argc; } -std::string -ZenCmdBase::FormatHttpResponse(const cpr::Response& Response) +static ReturnCode +GetReturnCodeFromHttpResult(int Error, HttpResponseCode ResponseCode) { - if (Response.error.code != cpr::ErrorCode::OK) + if ((cpr::ErrorCode)Error != cpr::ErrorCode::OK) { - if (Response.error.message.empty()) + switch ((cpr::ErrorCode)Error) { - return fmt::format("Request '{}' failed, error code {}", Response.url.str(), static_cast<int>(Response.error.code)); + case cpr::ErrorCode::CONNECTION_FAILURE: + return ReturnCode::kHttpCantConnectError; + case cpr::ErrorCode::HOST_RESOLUTION_FAILURE: + case cpr::ErrorCode::PROXY_RESOLUTION_FAILURE: + return ReturnCode::kHttpNoHost; + case cpr::ErrorCode::INTERNAL_ERROR: + case cpr::ErrorCode::NETWORK_RECEIVE_ERROR: + case cpr::ErrorCode::NETWORK_SEND_FAILURE: + case cpr::ErrorCode::OPERATION_TIMEDOUT: + return ReturnCode::kHttpTimeout; + case cpr::ErrorCode::SSL_CONNECT_ERROR: + case cpr::ErrorCode::SSL_LOCAL_CERTIFICATE_ERROR: + case cpr::ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR: + case cpr::ErrorCode::SSL_CACERT_ERROR: + case cpr::ErrorCode::GENERIC_SSL_ERROR: + return ReturnCode::kHttpSLLError; + default: + return ReturnCode::kHttpOtherClientError; } - return fmt::format("Request '{}' failed. Reason: '{}' ({})", - Response.url.str(), - Response.error.message, - static_cast<int>(Response.error.code)); } - - std::string Content; - if (auto It = Response.header.find("Content-Type"); It != Response.header.end()) - { - zen::HttpContentType ContentType = zen::ParseContentType(It->second); - if (ContentType == zen::HttpContentType::kText) - { - Content = Response.text; - } - else if (ContentType == zen::HttpContentType::kJSON) - { - Content = fmt::format("\n{}", Response.text); - } - else if (!Response.text.empty()) - { - Content = fmt::format("[{}]", MapContentTypeToString(ContentType)); - } - } - - std::string_view ResponseString = zen::ReasonStringForHttpResultCode( - Response.status_code == static_cast<long>(zen::HttpResponseCode::NoContent) ? static_cast<long>(zen::HttpResponseCode::OK) - : Response.status_code); - if (Content.empty()) - { - return std::string(ResponseString); - } - - return fmt::format("{}: {}", ResponseString, Content); -} - -int -ZenCmdBase::MapHttpToCommandReturnCode(const cpr::Response& Response) -{ - if (zen::IsHttpSuccessCode(Response.status_code)) + else if (IsHttpSuccessCode(ResponseCode)) { - return 0; + return ReturnCode::kSuccess; } - if (Response.error.code != cpr::ErrorCode::OK) + else { - return static_cast<int>(Response.error.code); + switch (ResponseCode) + { + case HttpResponseCode::Unauthorized: + return ReturnCode::kHttpUnauthorized; + case HttpResponseCode::NotFound: + return ReturnCode::kHttpNotFound; + case HttpResponseCode::Forbidden: + return ReturnCode::kHttpForbidden; + case HttpResponseCode::Conflict: + return ReturnCode::kHttpConflict; + case HttpResponseCode::InternalServerError: + return ReturnCode::kHttpInternalServerError; + case HttpResponseCode::ServiceUnavailable: + return ReturnCode::kHttpServiceUnavailable; + case HttpResponseCode::BadGateway: + return ReturnCode::kHttpBadGateway; + case HttpResponseCode::GatewayTimeout: + return ReturnCode::kHttpGatewayTimeout; + default: + if (ResponseCode >= HttpResponseCode::InternalServerError) + { + return ReturnCode::kHttpOtherServerError; + } + else + { + return ReturnCode::kHttpOtherClientError; + } + } } - return 1; } std::string @@ -945,7 +980,7 @@ main(int argc, char** argv) printf("\n"); } - exit(0); + return (int)ReturnCode::kSuccess; } #if ZEN_USE_SENTRY @@ -1022,86 +1057,107 @@ main(int argc, char** argv) { if (StrCaseCompare(SubCommand.c_str(), CmdInfo.CmdName) == 0) { - cxxopts::Options& VerbOptions = CmdInfo.Cmd->Options(); - try { - return CmdInfo.Cmd->Run(GlobalOptions, (int)CommandArgVec.size(), CommandArgVec.data()); + CmdInfo.Cmd->Run(GlobalOptions, (int)CommandArgVec.size(), CommandArgVec.data()); + return (int)ReturnCode::kSuccess; } catch (const OptionParseException& Ex) { - ZEN_CONSOLE_ERROR("Invalid arguments for command '{}': {}\n\n{}", SubCommand, Ex.what(), VerbOptions.help()); - exit(11); + ZEN_CONSOLE("{}\n", Ex.m_Help); + ZEN_CONSOLE_ERROR("Invalid arguments for command '{}': {}", SubCommand, Ex.what()); + return (int)ReturnCode::kBadInput; } catch (const std::system_error& Ex) { if (IsOOD(Ex)) { ZEN_CONSOLE_ERROR("Operation failed due to out of disk space: {}", Ex.what()); - exit(3); + return (int)ReturnCode::kOutOfDisk; } else if (IsOOM(Ex)) { ZEN_CONSOLE_ERROR("Operation failed due to out of memory: {}", Ex.what()); - exit(3); + return (int)ReturnCode::kOutOfMemory; } else { ZEN_CONSOLE_ERROR("Operation failed due to system error: {} ({})\n", Ex.what(), Ex.code() ? Ex.code().value() : 0); - exit(Ex.code() ? Ex.code().value() : 10); + return (int)ReturnCode::kOtherError; } } catch (const HttpClientError& Ex) { ZEN_CONSOLE_ERROR("Operation failed due to a http error: {}", Ex.what()); - exit(Ex.m_Error != 0 ? Ex.m_Error : (int)Ex.m_ResponseCode); + ReturnCode Result = GetReturnCodeFromHttpResult(Ex.m_Error, Ex.m_ResponseCode); + return (int)Result; + } + catch (const AssertException& Ex) + { + ZEN_CONSOLE_ERROR("Operation failed due to an assert exception: {}", Ex.FullDescription()); + return (int)ReturnCode::kAssertError; + } + catch (const ErrorWithReturnCode& Ex) + { + ZEN_CONSOLE_ERROR("{}", Ex.what()); + return Ex.m_ReturnCode; } catch (const std::exception& Ex) { - ZEN_CONSOLE_ERROR("Operation failed due to: {}\n", Ex.what()); - exit(11); + ZEN_CONSOLE_ERROR("{}\n", Ex.what()); + return (int)ReturnCode::kOtherError; } } } printf("Unknown command specified: '%s', exiting\n", SubCommand.c_str()); + return (int)ReturnCode::kBadInput; } catch (const OptionParseException& Ex) { - std::string HelpMessage = Options.help(); - - printf("Error parsing program arguments: %s\n\n%s", Ex.what(), HelpMessage.c_str()); - - return 9; + printf("%s\n\n", Ex.m_Help.c_str()); + printf("Invalid arguments arguments: %s", Ex.what()); + return (int)ReturnCode::kBadInput; } catch (const std::system_error& Ex) { if (IsOOD(Ex)) { printf("Operation failed due to out of disk space: %s", Ex.what()); - return 3; + return (int)ReturnCode::kOutOfDisk; } else if (IsOOM(Ex)) { printf("Operation failed due to out of memory: %s", Ex.what()); - return 3; + return (int)ReturnCode::kOutOfMemory; } else { printf("Operation failed due to system error: %s (%d)\n", Ex.what(), Ex.code() ? Ex.code().value() : 0); - return Ex.code() ? Ex.code().value() : 10; + return (int)ReturnCode::kOtherError; } } catch (const HttpClientError& Ex) { - printf("Operation failed due to a http error: %s", Ex.what()); - return Ex.m_Error != 0 ? Ex.m_Error : (int)Ex.m_ResponseCode; + printf("Error: Operation failed due to a http error: %s", Ex.what()); + ReturnCode Result = GetReturnCodeFromHttpResult(Ex.m_Error, Ex.m_ResponseCode); + return (int)Result; + } + catch (const AssertException& Ex) + { + printf("Error: Operation failed due to an assert exception: %s", Ex.FullDescription().c_str()); + return (int)ReturnCode::kAssertError; + } + catch (const ErrorWithReturnCode& Ex) + { + printf("Error: %s", Ex.what()); + return Ex.m_ReturnCode; } catch (const std::exception& Ex) { - printf("Operation failed due to: %s\n", Ex.what()); - return 11; + printf("Error: %s\n", Ex.what()); + return (int)ReturnCode::kOtherError; } - return 0; + return (int)ReturnCode::kSuccess; } |