refactor(core): use std error_code conditions
This commit is contained in:
@@ -3,13 +3,13 @@
|
|||||||
#include <expected>
|
#include <expected>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace cvmmap_streamer {
|
namespace cvmmap_streamer {
|
||||||
|
|
||||||
using error_t = int;
|
enum class ErrorCode : int {
|
||||||
|
|
||||||
enum class ErrorCode : error_t {
|
|
||||||
Ok = 0,
|
Ok = 0,
|
||||||
InvalidArgument,
|
InvalidArgument,
|
||||||
InvalidConfig,
|
InvalidConfig,
|
||||||
@@ -28,6 +28,90 @@ enum class ErrorCode : error_t {
|
|||||||
Internal,
|
Internal,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline std::string_view error_code_name(ErrorCode code) {
|
||||||
|
switch (code) {
|
||||||
|
case ErrorCode::Ok:
|
||||||
|
return "ok";
|
||||||
|
case ErrorCode::InvalidArgument:
|
||||||
|
return "invalid_argument";
|
||||||
|
case ErrorCode::InvalidConfig:
|
||||||
|
return "invalid_config";
|
||||||
|
case ErrorCode::Unsupported:
|
||||||
|
return "unsupported";
|
||||||
|
case ErrorCode::NotReady:
|
||||||
|
return "not_ready";
|
||||||
|
case ErrorCode::BackendUnavailable:
|
||||||
|
return "backend_unavailable";
|
||||||
|
case ErrorCode::AllocationFailed:
|
||||||
|
return "allocation_failed";
|
||||||
|
case ErrorCode::Io:
|
||||||
|
return "io";
|
||||||
|
case ErrorCode::Network:
|
||||||
|
return "network";
|
||||||
|
case ErrorCode::Protocol:
|
||||||
|
return "protocol";
|
||||||
|
case ErrorCode::Encoder:
|
||||||
|
return "encoder";
|
||||||
|
case ErrorCode::ExternalLibrary:
|
||||||
|
return "external_library";
|
||||||
|
case ErrorCode::Serialization:
|
||||||
|
return "serialization";
|
||||||
|
case ErrorCode::ChildProcess:
|
||||||
|
return "child_process";
|
||||||
|
case ErrorCode::EndOfStream:
|
||||||
|
return "end_of_stream";
|
||||||
|
case ErrorCode::Internal:
|
||||||
|
return "internal";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StreamerErrorCategory final : public std::error_category {
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
const char *name() const noexcept override {
|
||||||
|
return "cvmmap_streamer";
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string message(int code) const override {
|
||||||
|
return std::string(error_code_name(static_cast<ErrorCode>(code)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::error_condition default_error_condition(int code) const noexcept override {
|
||||||
|
switch (static_cast<ErrorCode>(code)) {
|
||||||
|
case ErrorCode::Ok:
|
||||||
|
return {};
|
||||||
|
case ErrorCode::InvalidArgument:
|
||||||
|
return std::make_error_condition(std::errc::invalid_argument);
|
||||||
|
case ErrorCode::Unsupported:
|
||||||
|
return std::make_error_condition(std::errc::not_supported);
|
||||||
|
case ErrorCode::AllocationFailed:
|
||||||
|
return std::make_error_condition(std::errc::not_enough_memory);
|
||||||
|
case ErrorCode::Io:
|
||||||
|
return std::make_error_condition(std::errc::io_error);
|
||||||
|
case ErrorCode::Protocol:
|
||||||
|
return std::make_error_condition(std::errc::protocol_error);
|
||||||
|
default:
|
||||||
|
return std::error_condition(code, *this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline const std::error_category &streamer_error_category() noexcept {
|
||||||
|
static const StreamerErrorCategory category{};
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline std::error_code make_error_code(ErrorCode code) noexcept {
|
||||||
|
return {static_cast<int>(code), streamer_error_category()};
|
||||||
|
}
|
||||||
|
|
||||||
inline constexpr ErrorCode ERR_OK = ErrorCode::Ok;
|
inline constexpr ErrorCode ERR_OK = ErrorCode::Ok;
|
||||||
inline constexpr ErrorCode ERR_INVALID_ARGUMENT = ErrorCode::InvalidArgument;
|
inline constexpr ErrorCode ERR_INVALID_ARGUMENT = ErrorCode::InvalidArgument;
|
||||||
inline constexpr ErrorCode ERR_INVALID_CONFIG = ErrorCode::InvalidConfig;
|
inline constexpr ErrorCode ERR_INVALID_CONFIG = ErrorCode::InvalidConfig;
|
||||||
@@ -46,7 +130,7 @@ inline constexpr ErrorCode ERR_END_OF_STREAM = ErrorCode::EndOfStream;
|
|||||||
inline constexpr ErrorCode ERR_INTERNAL = ErrorCode::Internal;
|
inline constexpr ErrorCode ERR_INTERNAL = ErrorCode::Internal;
|
||||||
|
|
||||||
struct Error {
|
struct Error {
|
||||||
ErrorCode code{ERR_OK};
|
std::error_code code{make_error_code(ERR_OK)};
|
||||||
std::string detail{};
|
std::string detail{};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,7 +140,7 @@ using Result = std::expected<T, Error>;
|
|||||||
using Status = Result<void>;
|
using Status = Result<void>;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline std::unexpected<Error> unexpected_error(ErrorCode code, std::string detail = {}) {
|
inline std::unexpected<Error> unexpected_error(std::error_code code, std::string detail = {}) {
|
||||||
return std::unexpected(Error{
|
return std::unexpected(Error{
|
||||||
.code = code,
|
.code = code,
|
||||||
.detail = std::move(detail),
|
.detail = std::move(detail),
|
||||||
@@ -64,51 +148,117 @@ inline std::unexpected<Error> unexpected_error(ErrorCode code, std::string detai
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline std::string_view error_code_name(ErrorCode code) {
|
inline std::unexpected<Error> unexpected_error(ErrorCode code, std::string detail = {}) {
|
||||||
switch (code) {
|
return unexpected_error(make_error_code(code), std::move(detail));
|
||||||
case ERR_OK:
|
|
||||||
return "ok";
|
|
||||||
case ERR_INVALID_ARGUMENT:
|
|
||||||
return "invalid_argument";
|
|
||||||
case ERR_INVALID_CONFIG:
|
|
||||||
return "invalid_config";
|
|
||||||
case ERR_UNSUPPORTED:
|
|
||||||
return "unsupported";
|
|
||||||
case ERR_NOT_READY:
|
|
||||||
return "not_ready";
|
|
||||||
case ERR_BACKEND_UNAVAILABLE:
|
|
||||||
return "backend_unavailable";
|
|
||||||
case ERR_ALLOCATION_FAILED:
|
|
||||||
return "allocation_failed";
|
|
||||||
case ERR_IO:
|
|
||||||
return "io";
|
|
||||||
case ERR_NETWORK:
|
|
||||||
return "network";
|
|
||||||
case ERR_PROTOCOL:
|
|
||||||
return "protocol";
|
|
||||||
case ERR_ENCODER:
|
|
||||||
return "encoder";
|
|
||||||
case ERR_EXTERNAL_LIBRARY:
|
|
||||||
return "external_library";
|
|
||||||
case ERR_SERIALIZATION:
|
|
||||||
return "serialization";
|
|
||||||
case ERR_CHILD_PROCESS:
|
|
||||||
return "child_process";
|
|
||||||
case ERR_END_OF_STREAM:
|
|
||||||
return "end_of_stream";
|
|
||||||
case ERR_INTERNAL:
|
|
||||||
return "internal";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool has_error_code(const std::error_code &code, ErrorCode expected) noexcept {
|
||||||
|
return code == make_error_code(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool has_error_code(const Error &error, ErrorCode expected) noexcept {
|
||||||
|
return has_error_code(error.code, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool has_error_condition(const std::error_code &code, std::errc condition) noexcept {
|
||||||
|
return code == condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool has_error_condition(const Error &error, std::errc condition) noexcept {
|
||||||
|
return has_error_condition(error.code, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_invalid_argument_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_condition(code, std::errc::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_invalid_argument_error(const Error &error) noexcept {
|
||||||
|
return is_invalid_argument_error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_unsupported_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_condition(code, std::errc::not_supported);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_unsupported_error(const Error &error) noexcept {
|
||||||
|
return is_unsupported_error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_allocation_failed_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_condition(code, std::errc::not_enough_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_allocation_failed_error(const Error &error) noexcept {
|
||||||
|
return is_allocation_failed_error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_io_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_condition(code, std::errc::io_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_io_error(const Error &error) noexcept {
|
||||||
|
return is_io_error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_protocol_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_condition(code, std::errc::protocol_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_protocol_error(const Error &error) noexcept {
|
||||||
|
return is_protocol_error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_not_ready_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_code(code, ERR_NOT_READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_not_ready_error(const Error &error) noexcept {
|
||||||
|
return is_not_ready_error(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_end_of_stream_error(const std::error_code &code) noexcept {
|
||||||
|
return has_error_code(code, ERR_END_OF_STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_end_of_stream_error(const Error &error) noexcept {
|
||||||
|
return is_end_of_stream_error(error.code);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline std::string format_error(const Error &error) {
|
inline std::string format_error(const Error &error) {
|
||||||
|
const auto message = error.code.message();
|
||||||
if (error.detail.empty()) {
|
if (error.detail.empty()) {
|
||||||
return std::string(error_code_name(error.code));
|
return message;
|
||||||
}
|
}
|
||||||
return std::string(error_code_name(error.code)) + ": " + error.detail;
|
if (error.code.category() == streamer_error_category()) {
|
||||||
|
return message + ": " + error.detail;
|
||||||
|
}
|
||||||
|
return error.detail + ": " + message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_error_code_enum<::cvmmap_streamer::ErrorCode> : true_type {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -213,7 +213,10 @@ Status write_all(int fd, std::span<const std::uint8_t> bytes) {
|
|||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return unexpected_error(ERR_IO, std::strerror(errno));
|
const auto error = errno;
|
||||||
|
return unexpected_error(
|
||||||
|
std::error_code(error, std::generic_category()),
|
||||||
|
"failed to write to ffmpeg process stdin");
|
||||||
}
|
}
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return unexpected_error(ERR_IO, "short write to ffmpeg process stdin");
|
return unexpected_error(ERR_IO, "short write to ffmpeg process stdin");
|
||||||
@@ -530,14 +533,20 @@ private:
|
|||||||
Result<Session> spawn_session(const std::string &url) const {
|
Result<Session> spawn_session(const std::string &url) const {
|
||||||
int stdin_pipe[2]{-1, -1};
|
int stdin_pipe[2]{-1, -1};
|
||||||
if (pipe(stdin_pipe) != 0) {
|
if (pipe(stdin_pipe) != 0) {
|
||||||
return unexpected_error(ERR_IO, "failed to create ffmpeg stdin pipe: " + std::string(std::strerror(errno)));
|
const auto error = errno;
|
||||||
|
return unexpected_error(
|
||||||
|
std::error_code(error, std::generic_category()),
|
||||||
|
"failed to create ffmpeg stdin pipe");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto child = fork();
|
const auto child = fork();
|
||||||
if (child < 0) {
|
if (child < 0) {
|
||||||
|
const auto error = errno;
|
||||||
close(stdin_pipe[0]);
|
close(stdin_pipe[0]);
|
||||||
close(stdin_pipe[1]);
|
close(stdin_pipe[1]);
|
||||||
return unexpected_error(ERR_CHILD_PROCESS, "failed to fork ffmpeg child: " + std::string(std::strerror(errno)));
|
return unexpected_error(
|
||||||
|
std::error_code(error, std::generic_category()),
|
||||||
|
"failed to fork ffmpeg child");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
@@ -626,7 +635,10 @@ private:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (wait_result < 0) {
|
if (wait_result < 0) {
|
||||||
return unexpected_error(ERR_CHILD_PROCESS, "failed to poll ffmpeg child: " + std::string(std::strerror(errno)));
|
const auto error = errno;
|
||||||
|
return unexpected_error(
|
||||||
|
std::error_code(error, std::generic_category()),
|
||||||
|
"failed to poll ffmpeg child");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.stdin_fd >= 0) {
|
if (session.stdin_fd >= 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user