diff options
author | Brian Lopez <seniorlopez@gmail.com> | 2018-01-12 15:13:40 -0800 |
---|---|---|
committer | Brian Lopez <seniorlopez@gmail.com> | 2018-01-13 12:18:17 -0800 |
commit | d01759f5d1430fe663bbcd333389cb2556c8d0a9 (patch) | |
tree | cabfac2b045ae191c538d73b2f6e7491dedf9f59 | |
parent | 5734768b9edaecedaa7b13fed2ce59cb588df7e8 (diff) | |
download | libgit2-brianmario/trailer-info.tar.gz |
Add message trailer iterator APIbrianmario/trailer-info
This also moves the existing callback-based API over to wrap the
new iterator API.
-rw-r--r-- | include/git2/message.h | 13 | ||||
-rw-r--r-- | src/trailer.c | 131 | ||||
-rw-r--r-- | tests/message/trailer.c | 268 |
3 files changed, 374 insertions, 38 deletions
diff --git a/include/git2/message.h b/include/git2/message.h index a74dcc2d9..d5229660d 100644 --- a/include/git2/message.h +++ b/include/git2/message.h @@ -58,6 +58,19 @@ typedef int(*git_message_trailer_cb)(const char *key, const char *value, void *p */ GIT_EXTERN(int) git_message_trailers(const char *message, git_message_trailer_cb cb, void *payload); +typedef struct git_message_trailer_iterator git_message_trailer_iterator; + +GIT_EXTERN(int) git_message_trailer_iterator_new( + git_message_trailer_iterator **out, + const char *message); + +GIT_EXTERN(int) git_message_trailer_iterator_next( + const char **key_out, + const char **value_out, + git_message_trailer_iterator *iter); + +GIT_EXTERN(void) git_message_trailer_iterator_free(git_message_trailer_iterator *iter); + /** @} */ GIT_END_DECL diff --git a/src/trailer.c b/src/trailer.c index cfefc530c..fd8bd5c8b 100644 --- a/src/trailer.c +++ b/src/trailer.c @@ -248,7 +248,7 @@ static int find_trailer_end(const char *buf, size_t len) return len - ignore_non_trailer(buf, len); } -static char *find_trailer(const char *message, size_t* len) +static char *extract_trailer_block(const char *message, size_t* len) { size_t patch_start = find_patch_start(message); size_t trailer_end = find_trailer_end(message, patch_start); @@ -276,48 +276,84 @@ enum trailer_state { S_IGNORE = 7, }; -#define NEXT(st) { state = (st); ptr++; continue; } +typedef struct { + char *trailer_block; + size_t block_len; + char *ptr; +} trailer_iterator; + +int git_message_trailer_iterator_new( + git_message_trailer_iterator **out, + const char *message) +{ + trailer_iterator *iter; + + iter = git__calloc(1, sizeof(trailer_iterator)); + GITERR_CHECK_ALLOC(iter); + + iter->trailer_block = extract_trailer_block(message, &iter->block_len); + iter->ptr = iter->trailer_block; + + *out = (git_message_trailer_iterator *) iter; + + return 0; +} + +void git_message_trailer_iterator_free(git_message_trailer_iterator *_iter) +{ + trailer_iterator *iter = (trailer_iterator *) _iter; + + if (iter == NULL) + return; + + git__free(iter->trailer_block); + git__free(iter); +} + +#define NEXT(st) { state = (st); iter->ptr++; continue; } #define GOTO(st) { state = (st); continue; } -int git_message_trailers(const char *message, git_message_trailer_cb cb, void *payload) +int git_message_trailer_iterator_next( + const char **key_out, + const char **value_out, + git_message_trailer_iterator *_iter) { + trailer_iterator *iter = (trailer_iterator *) _iter; enum trailer_state state = S_START; int rc = 0; - char *ptr; - char *key = NULL; - char *value = NULL; - size_t trailer_len; - char *trailer = find_trailer(message, &trailer_len); + if (*iter->ptr == 0) { + return GIT_ITEROVER; + } - for (ptr = trailer;;) { + while (iter->ptr != 0) { switch (state) { case S_START: { - if (*ptr == 0) { + if (*iter->ptr == 0) { goto ret; } - key = ptr; + *key_out = iter->ptr; GOTO(S_KEY); } case S_KEY: { - if (*ptr == 0) { + if (*iter->ptr == 0) { goto ret; } - if (isalnum(*ptr) || *ptr == '-') { + if (isalnum(*iter->ptr) || *iter->ptr == '-') { // legal key character NEXT(S_KEY); } - if (*ptr == ' ' || *ptr == '\t') { + if (*iter->ptr == ' ' || *iter->ptr == '\t') { // optional whitespace before separator - *ptr = 0; + *iter->ptr = 0; NEXT(S_KEY_WS); } - if (strchr(TRAILER_SEPARATORS, *ptr)) { - *ptr = 0; + if (strchr(TRAILER_SEPARATORS, *iter->ptr)) { + *iter->ptr = 0; NEXT(S_SEP_WS); } @@ -325,15 +361,15 @@ int git_message_trailers(const char *message, git_message_trailer_cb cb, void *p GOTO(S_IGNORE); } case S_KEY_WS: { - if (*ptr == 0) { + if (*iter->ptr == 0) { goto ret; } - if (*ptr == ' ' || *ptr == '\t') { + if (*iter->ptr == ' ' || *iter->ptr == '\t') { NEXT(S_KEY_WS); } - if (strchr(TRAILER_SEPARATORS, *ptr)) { + if (strchr(TRAILER_SEPARATORS, *iter->ptr)) { NEXT(S_SEP_WS); } @@ -341,53 +377,46 @@ int git_message_trailers(const char *message, git_message_trailer_cb cb, void *p GOTO(S_IGNORE); } case S_SEP_WS: { - if (*ptr == 0) { + if (*iter->ptr == 0) { goto ret; } - if (*ptr == ' ' || *ptr == '\t') { + if (*iter->ptr == ' ' || *iter->ptr == '\t') { NEXT(S_SEP_WS); } - value = ptr; + *value_out = iter->ptr; NEXT(S_VALUE); } case S_VALUE: { - if (*ptr == 0) { + if (*iter->ptr == 0) { GOTO(S_VALUE_END); } - if (*ptr == '\n') { + if (*iter->ptr == '\n') { NEXT(S_VALUE_NL); } NEXT(S_VALUE); } case S_VALUE_NL: { - if (*ptr == ' ') { + if (*iter->ptr == ' ') { // continuation; NEXT(S_VALUE); } - ptr[-1] = 0; + iter->ptr[-1] = 0; GOTO(S_VALUE_END); } case S_VALUE_END: { - if ((rc = cb(key, value, payload))) { - goto ret; - } - - key = NULL; - value = NULL; - - GOTO(S_START); + goto ret; } case S_IGNORE: { - if (*ptr == 0) { + if (*iter->ptr == 0) { goto ret; } - if (*ptr == '\n') { + if (*iter->ptr == '\n') { NEXT(S_START); } @@ -397,6 +426,32 @@ int git_message_trailers(const char *message, git_message_trailer_cb cb, void *p } ret: - git__free(trailer); return rc; } + +int git_message_trailers(const char *message, git_message_trailer_cb cb, void *payload) +{ + git_message_trailer_iterator *iterator; + int rc; + + if ((rc = git_message_trailer_iterator_new(&iterator, message))) { + return rc; + } + + while (rc != GIT_ITEROVER) { + const char *key; + const char *value; + + if ((rc = git_message_trailer_iterator_next(&key, &value, iterator))) { + goto ret; + } + + if ((rc = cb(key, value, payload))) { + goto ret; + } + } + +ret: + git_message_trailer_iterator_free(iterator); + return 0; +} diff --git a/tests/message/trailer.c b/tests/message/trailer.c index 83fc77808..85949404c 100644 --- a/tests/message/trailer.c +++ b/tests/message/trailer.c @@ -177,3 +177,271 @@ void test_message_trailer__invalid(void) "Another: trailer\n" , trailers); } + +void test_message_trailer__iterator_simple(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Signed-off-by: foo@bar.com\n" + "Signed-off-by: someone@else.com\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Signed-off-by", key); + cl_assert_equal_s("foo@bar.com", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Signed-off-by", key); + cl_assert_equal_s("someone@else.com", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_no_whitespace(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Key:value\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Key", key); + cl_assert_equal_s("value", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_no_newline(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Key:value"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Key", key); + cl_assert_equal_s("value", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_not_last_paragraph(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Key: value\n" + "\n" + "More stuff\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_conflicts(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Key: value\n" + "\n" + "Conflicts:\n" + "\tfoo.c\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Key", key); + cl_assert_equal_s("value", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_patch(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Key: value\n" + "\n" + "---\n" + "More: stuff\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Key", key); + cl_assert_equal_s("value", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_continuation(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "A: b\n" + " c\n" + "D: e\n" + " f: g h\n" + "I: j\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("A", key); + cl_assert_equal_s("b\n c", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("D", key); + cl_assert_equal_s("e\n f: g h", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("I", key); + cl_assert_equal_s("j", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} + +void test_message_trailer__iterator_invalid(void) +{ + git_message_trailer_iterator *iterator; + const char *key; + const char *value; + int rc; + + rc = git_message_trailer_iterator_new( + &iterator, + "Message\n" + "\n" + "Signed-off-by: some@one.com\n" + "Not a trailer\n" + "Another: trailer\n"); + + cl_assert(rc == 0); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Signed-off-by", key); + cl_assert_equal_s("some@one.com", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == 0); + + cl_assert_equal_s("Another", key); + cl_assert_equal_s("trailer", value); + + rc = git_message_trailer_iterator_next(&key, &value, iterator); + + cl_assert(rc == GIT_ITEROVER); + + git_message_trailer_iterator_free(iterator); +} |