packages feed

eventlog-socket-0.1.2.0: cbits/eventlog_socket/error.h

#ifndef EVENTLOG_SOCKET_ERR_H
#define EVENTLOG_SOCKET_ERR_H

#include "./debug.h"

/// @brief Construct a status from an `EventlogSocketStatusCode` status code.
#define STATUS_FROM_CODE(status_code)                                          \
  ((EventlogSocketStatus){.ess_status_code = (status_code),                    \
                          .ess_error_code = 0})

/// @brief Construct a status from `errno`.
#define STATUS_FROM_ERRNO()                                                    \
  ((EventlogSocketStatus){.ess_status_code = EVENTLOG_SOCKET_ERR_SYS,          \
                          .ess_error_code = errno})

/// @brief Construct a status from a `pthread.h` error code.
#define STATUS_FROM_PTHREAD(pthread_errno)                                     \
  ((EventlogSocketStatus){.ess_status_code = EVENTLOG_SOCKET_ERR_SYS,          \
                          .ess_error_code = (pthread_errno)})

/// @brief Construct a status from a `getaddrinfo` error code.
#define STATUS_FROM_GAI_ERROR(gai_error)                                       \
  ((EventlogSocketStatus){.ess_status_code = EVENTLOG_SOCKET_ERR_GAI,          \
                          .ess_error_code = (gai_error)})

/// @brief A condition that evaluates to @c true if @p expr is an error.
///
/// @warning This macro evaluates its first argument multiple times.
#define STATUS_IS_ERROR(expr)                                                  \
  /* If none of the following are true... */                                   \
  (!((/* ...the status is an OK. */                                            \
      (expr).ess_status_code == EVENTLOG_SOCKET_OK) ||                         \
     (/* ...the status is a successful getaddrinfo exit code. */               \
      (expr).ess_status_code == EVENTLOG_SOCKET_ERR_GAI &&                     \
      (expr).ess_error_code == 0) ||                                           \
     (/* ...the status is a successful system call exit code. */               \
      (expr).ess_status_code == EVENTLOG_SOCKET_ERR_SYS &&                     \
      (expr).ess_error_code == 0)))

/// @brief If the @p expr returns an error status, immediately return it.
#define RETURN_ON_ERROR_CLEANUP(expr, cleanup)                                 \
  do {                                                                         \
    const EventlogSocketStatus status = (expr);                                \
    if (STATUS_IS_ERROR(status)) {                                             \
      char *strerr = eventlog_socket_strerror(status);                         \
      DEBUG_ERROR("%s", strerr);                                               \
      free(strerr);                                                            \
      (cleanup);                                                               \
      return status;                                                           \
    }                                                                          \
  } while (0)

/// @brief If the @p expr returns an error status, immediately return it.
#define RETURN_ON_ERROR(expr) RETURN_ON_ERROR_CLEANUP(expr, (void)0)

/// @brief If the @p expr returns an error status, save, cleanup, and exit.
///
/// @warning This macro assumes the existence of two global variables `g_status`
/// and `g_status_mutex`.
#define EXIT_ON_ERROR_CLEANUP(expr, cleanup)                                   \
  do {                                                                         \
    const EventlogSocketStatus status = (expr);                                \
    if (STATUS_IS_ERROR(status)) {                                             \
      char *strerr = eventlog_socket_strerror(status);                         \
      DEBUG_ERROR("%s", strerr);                                               \
      free(strerr);                                                            \
      pthread_mutex_lock(&g_status_mutex);                                     \
      memcpy(&g_status, &status, sizeof(EventlogSocketStatus));                \
      pthread_mutex_unlock(&g_status_mutex);                                   \
      (cleanup);                                                               \
      pthread_exit(NULL);                                                      \
    }                                                                          \
  } while (0)

/// @brief If the @p expr returns an error status, save and exit.
///
/// @warning This macro assumes the existence of two global variables `g_status`
/// and `g_status_mutex`.
#define EXIT_ON_ERROR(expr) EXIT_ON_ERROR_CLEANUP(expr, (void)0)

#endif /* EVENTLOG_SOCKET_ERR_H */