Encode error type in argpar_error
authorSimon Marchi <simon.marchi@efficios.com>
Tue, 24 Aug 2021 20:31:19 +0000 (16:31 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 25 Aug 2021 03:25:05 +0000 (23:25 -0400)
With the current API, when encountering an argument parsing error, the
error type (unknown option, missing option argument, unexpected option
argument) is encoded in the returned argpar_iter_next_status value.
In my experience it would be more useful if that information was encoded
in the argpar_error instead.

Imagine a helper function `format_arg_error` that formats an argument
parsing error based on a particular project's needs.  That function
needs to take the argpar_iter_next_status along with the argpar_error:

    char *format_arg_error(enum argpar_iter_next_status status,
    const struct argpar_error *error);

It would be simpler to have the argpar_error object know what type of
error was returned.  That makes the argpar_error contain all the
necessary information about the error.

So, the proposed changes are:

 - Replace ARGPAR_ITER_NEXT_STATUS_ERROR_* with a single
   ARGPAR_ITER_NEXT_STATUS_ERROR, indicating that an argument parsing
   error happened.
 - Add a new `enum argpar_error_type` type, to represent the 3 argument
   parsing error types.
 - Add a new `type` property to argpar_error, obtained through
   `argpar_error_get_type`.

Note that the argpar_error out parameter of the argpar_iter_next
function is optional.  This means that the user can currently receive an
error status (ARGPAR_ITER_NEXT_STATUS_ERROR_{UNKNOWN_OPT,
UNEXPECTED_OPT_ARG,MISSING_OP_ARG}) but not have any more details about
the error.  I don't think this is really useful, because the user of the
API wouldn't be able to do make a useful error message by knowing the
type of error but not having the corresponding argpar_error object.

API documentation left to Philippe Proulx.

Change-Id: Ice4db8852030ccd3ba9cbdf3717d9850b8ee7935

argpar/argpar.c
argpar/argpar.h
tests/test_argpar.c

index 55995124fb6332e5a4a3cf8296b55d55adfde3b2..10f6ee8d42e93a780a65ad4c5cf66347d667c42d 100644 (file)
@@ -103,6 +103,9 @@ struct argpar_item_non_opt {
 
 /* Parsing error */
 struct argpar_error {
+       /* Error type */
+       enum argpar_error_type type;
+
        /* Original argument index */
        unsigned int orig_index;
 
@@ -265,6 +268,7 @@ end:
  */
 static
 int set_error(struct argpar_error ** const error,
+               enum argpar_error_type type,
                const char * const unknown_opt_name,
                const struct argpar_opt_descr * const opt_descr,
                const bool is_short)
@@ -280,6 +284,8 @@ int set_error(struct argpar_error ** const error,
                goto error;
        }
 
+       (*error)->type = type;
+
        if (unknown_opt_name) {
                (*error)->unknown_opt_name = ARGPAR_CALLOC(char,
                        strlen(unknown_opt_name) + 1 + (is_short ? 1 : 2));
@@ -308,6 +314,14 @@ end:
        return ret;
 }
 
+ARGPAR_HIDDEN
+enum argpar_error_type argpar_error_type(
+               const struct argpar_error * const error)
+{
+       ARGPAR_ASSERT(error);
+       return error->type;
+}
+
 ARGPAR_HIDDEN
 unsigned int argpar_error_orig_index(const struct argpar_error * const error)
 {
@@ -320,6 +334,7 @@ const char *argpar_error_unknown_opt_name(
                const struct argpar_error * const error)
 {
        ARGPAR_ASSERT(error);
+       ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_UNKNOWN_OPT);
        ARGPAR_ASSERT(error->unknown_opt_name);
        return error->unknown_opt_name;
 }
@@ -329,6 +344,8 @@ const struct argpar_opt_descr *argpar_error_opt_descr(
                const struct argpar_error * const error, bool * const is_short)
 {
        ARGPAR_ASSERT(error);
+       ARGPAR_ASSERT(error->type == ARGPAR_ERROR_TYPE_MISSING_OPT_ARG ||
+               error->type == ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG);
        ARGPAR_ASSERT(error->opt_descr);
 
        if (is_short) {
@@ -384,10 +401,8 @@ end:
 /* Return type of parse_short_opt_group() and parse_long_opt() */
 enum parse_orig_arg_opt_ret {
        PARSE_ORIG_ARG_OPT_RET_OK,
-       PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -1,
-       PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG = -2,
-       PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG = -4,
-       PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -5,
+       PARSE_ORIG_ARG_OPT_RET_ERROR = -1,
+       PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -2,
 };
 
 /*
@@ -426,9 +441,10 @@ enum parse_orig_arg_opt_ret parse_short_opt_group(
                const char unknown_opt_name[] =
                        {*iter->short_opt_group_ch, '\0'};
 
-               ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
 
-               if (set_error(error, unknown_opt_name, NULL, true)) {
+               if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
+                               unknown_opt_name, NULL, true)) {
                        ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
                }
 
@@ -451,9 +467,10 @@ enum parse_orig_arg_opt_ret parse_short_opt_group(
                 */
                if (!opt_arg || (iter->short_opt_group_ch[1] &&
                                strlen(opt_arg) == 0)) {
-                       ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG;
+                       ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
 
-                       if (set_error(error, NULL, descr, true)) {
+                       if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
+                                       NULL, descr, true)) {
                                ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
                        }
 
@@ -547,9 +564,10 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
        /* Find corresponding option descriptor */
        descr = find_descr(descrs, '\0', long_opt_name);
        if (!descr) {
-               ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
 
-               if (set_error(error, long_opt_name, NULL, false)) {
+               if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
+                               long_opt_name, NULL, false)) {
                        ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
                }
 
@@ -564,9 +582,10 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
                } else {
                        /* `--long-opt arg` style */
                        if (!next_orig_arg) {
-                               ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG;
+                               ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
 
-                               if (set_error(error, NULL, descr, false)) {
+                               if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
+                                               NULL, descr, false)) {
                                        ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
                                }
 
@@ -581,9 +600,10 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
                 * Unexpected `--opt=arg` style for a long option which
                 * doesn't accept an argument.
                 */
-               ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG;
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
 
-               if (set_error(error, NULL, descr, false)) {
+               if (set_error(error, ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG,
+                               NULL, descr, false)) {
                        ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY;
                }
 
@@ -735,28 +755,12 @@ enum argpar_iter_next_status argpar_iter_next(
        case PARSE_ORIG_ARG_OPT_RET_OK:
                status = ARGPAR_ITER_NEXT_STATUS_OK;
                break;
-       case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT:
-       case PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG:
-       case PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG:
+       case PARSE_ORIG_ARG_OPT_RET_ERROR:
                if (error) {
                        ARGPAR_ASSERT(*error);
                        (*nc_error)->orig_index = iter->i;
                }
-
-               switch (parse_orig_arg_opt_ret) {
-               case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT:
-                       status = ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT;
-                       break;
-               case PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG:
-                       status = ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG;
-                       break;
-               case PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG:
-                       status = ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG;
-                       break;
-               default:
-                       abort();
-               }
-
+               status = ARGPAR_ITER_NEXT_STATUS_ERROR;
                break;
        case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY:
                status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;
index 3340d90f593031756a6e8d80f5a704ddff6f89a1..07ec8c47071f8f0f850e7d41020faa8d66361688 100644 (file)
@@ -338,6 +338,17 @@ void argpar_item_destroy(const struct argpar_item *item);
 @{
 */
 
+enum argpar_error_type {
+       /// Unknown option error
+       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
+
+       /// Missing option argument error
+       ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
+
+       /// Unexpected option argument error
+       ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG,
+};
+
 /*!
 @struct argpar_error
 
@@ -346,6 +357,10 @@ void argpar_item_destroy(const struct argpar_item *item);
 */
 struct argpar_error;
 
+ARGPAR_HIDDEN
+enum argpar_error_type argpar_error_type(
+       const struct argpar_error *error);
+
 /*!
 @brief
     Returns the index of the original argument (in \p argv, as passed to
@@ -603,14 +618,8 @@ enum argpar_iter_next_status {
        /// End of iteration (no more original arguments to parse)
        ARGPAR_ITER_NEXT_STATUS_END,
 
-       /// Unknown option error
-       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT = -1,
-
-       /// Missing option argument error
-       ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG = -2,
-
-       /// Unexpected option argument error
-       ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG = -3,
+       /// Argument error
+       ARGPAR_ITER_NEXT_STATUS_ERROR = -1,
 
        /// Memory error
        ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY = -12,
index bd94f3ff034db28c8e41aa9b117584cd8db395eb..87646a9a0a9d99fcc28a6ededbc035eee4eddeae 100644 (file)
@@ -528,7 +528,7 @@ void succeed_tests(void)
  */
 static
 void test_fail(const char * const cmdline,
-               const enum argpar_iter_next_status expected_status,
+               const enum argpar_error_type expected_error_type,
                const unsigned int expected_orig_index,
                const char * const expected_unknown_opt_name,
                const unsigned int expected_opt_descr_index,
@@ -551,10 +551,11 @@ void test_fail(const char * const cmdline,
                ARGPAR_ITEM_DESTROY_AND_RESET(item);
                status = argpar_iter_next(iter, &item, &error);
                ok(status == ARGPAR_ITER_NEXT_STATUS_OK ||
-                       status == expected_status,
+                       (status == ARGPAR_ITER_NEXT_STATUS_ERROR &&
+                               argpar_error_type(error) == expected_error_type),
                        "argpar_iter_next() returns the expected status "
-                       "(%d) for command line `%s` (call %u)",
-                       status, cmdline, i + 1);
+                       "and error type (%d) for command line `%s` (call %u)",
+                       expected_error_type, cmdline, i + 1);
 
                if (status != ARGPAR_ITER_NEXT_STATUS_OK) {
                        ok(!item,
@@ -576,7 +577,7 @@ void test_fail(const char * const cmdline,
                                "for command line `%s` (call %u)",
                                cmdline, i + 1);
 
-                       if (status == ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       if (argpar_error_type(error) == ARGPAR_ERROR_TYPE_UNKNOWN_OPT) {
                                ok(strcmp(argpar_error_unknown_opt_name(error),
                                        expected_unknown_opt_name) == 0,
                                        "argpar_iter_next() sets an error with "
@@ -643,7 +644,7 @@ void fail_tests(void)
 
                test_fail(
                        "-d salut -e -d meow",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+                       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
                        2, "-e", 0, false,
                        descrs);
        }
@@ -657,7 +658,7 @@ void fail_tests(void)
 
                test_fail(
                        "-dsalut -e -d meow",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+                       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
                        1, "-e", 0, false,
                        descrs);
        }
@@ -671,7 +672,7 @@ void fail_tests(void)
 
                test_fail(
                        "--sink party --food --sink impulse",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+                       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
                        2, "--food", 0, false,
                        descrs);
        }
@@ -685,7 +686,7 @@ void fail_tests(void)
 
                test_fail(
                        "--sink=party --food --sink=impulse",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+                       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
                        1, "--food", 0, false,
                        descrs);
        }
@@ -699,7 +700,7 @@ void fail_tests(void)
 
                test_fail(
                        "--thumb=party --food=18 bateau --thumb waves",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+                       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
                        1, "--food", 0, false,
                        descrs);
        }
@@ -713,7 +714,7 @@ void fail_tests(void)
 
                test_fail(
                        "--thumb=party wound --food --thumb waves",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+                       ARGPAR_ERROR_TYPE_UNKNOWN_OPT,
                        2, "--food", 0, false,
                        descrs);
        }
@@ -727,7 +728,7 @@ void fail_tests(void)
 
                test_fail(
                        "allo --thumb",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG,
+                       ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
                        1, NULL, 0, false,
                        descrs);
        }
@@ -741,7 +742,7 @@ void fail_tests(void)
 
                test_fail(
                        "zoom heille -k",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG,
+                       ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
                        2, NULL, 0, true,
                        descrs);
        }
@@ -757,7 +758,7 @@ void fail_tests(void)
 
                test_fail(
                        "-abc",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG,
+                       ARGPAR_ERROR_TYPE_MISSING_OPT_ARG,
                        0, NULL, 2, true,
                        descrs);
        }
@@ -771,7 +772,7 @@ void fail_tests(void)
 
                test_fail(
                        "ambulance --chevre=fromage tar -cjv",
-                       ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG,
+                       ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG,
                        1, NULL, 0, false,
                        descrs);
        }
This page took 0.02825 seconds and 4 git commands to generate.