From: Simon Marchi Date: Tue, 24 Aug 2021 20:31:19 +0000 (-0400) Subject: Encode error type in argpar_error X-Git-Url: http://git.efficios.com/?p=argpar.git;a=commitdiff_plain;h=10aefab2119c89f4afa4f46a619eae20d6be7512;hp=0a024790c9bd035f94db7919cafc0ffd051452f3 Encode error type in argpar_error 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 --- diff --git a/argpar/argpar.c b/argpar/argpar.c index 5599512..10f6ee8 100644 --- a/argpar/argpar.c +++ b/argpar/argpar.c @@ -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; diff --git a/argpar/argpar.h b/argpar/argpar.h index 3340d90..07ec8c4 100644 --- a/argpar/argpar.h +++ b/argpar/argpar.h @@ -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, diff --git a/tests/test_argpar.c b/tests/test_argpar.c index bd94f3f..87646a9 100644 --- a/tests/test_argpar.c +++ b/tests/test_argpar.c @@ -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); }