summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrb0nk500 <b0nk@b0nk.xyz>2022-07-28 13:54:05 -0300
committermrb0nk500 <b0nk@b0nk.xyz>2022-07-28 13:54:05 -0300
commit2ddb5741c9d6c8a0942e0a73f66d21617cf378e8 (patch)
treeee13302392deaaac17649b524e22323427c3e569
parentd55488c605c425815c641f22aa46c398da20a950 (diff)
git: Implement all of `get_pull_request()`
`get_pull_request()` takes an index, and the main PR root, and returns the PR of the supplied index if it exists, or NULL if no PR with that index exists.
-rw-r--r--git.c244
-rw-r--r--git.h29
2 files changed, 272 insertions, 1 deletions
diff --git a/git.c b/git.c
index ed4805a..7124a4d 100644
--- a/git.c
+++ b/git.c
@@ -72,8 +72,250 @@ void free_files(file **files) {
free(files);
}
-pull_request *get_pull_request(int id, const char *root) {
+static linked_list *parse_dsv_str(const char *str, const char *delm) {
+ linked_list *tail = NULL;
+ for (; *str != '\0'; str += strspn(str, delm)) {
+ /* Get the length of the token. */
+ size_t tok_len = strcspn(str, delm);
+ /* Create the token. */
+ char *tok = calloc(tok_len+1, sizeof(char));
+ /* Get the token from the string. */
+ memcpy(tok, tmp, tok_len);
+ /* Add the token to the list.. */
+ tail = add_node(&tail, tok);
+ /* Move over the token. */
+ str += tok_len;
+ }
+ return tail;
+}
+
+static void parse_patch_list(void *ctx, void *ret, const keyword *key, keyword_val val) {
+ /* Do we actually have a "patches" keyword? */
+ if (key->type == TYPE_STRING) {
+ const char *root = (const char *)ctx;
+ pull_request *pr = (pull_request *)ret;
+ /* Is the supplied string non-empty? */
+ if (!is_empty(val.str)) {
+ /* Parse the patch list. */
+ linked_list *patch_list = parse_dsv_str(val.str, " \t\v");
+ /* Get the number of patch files in the list. */
+ int num_patches = linked_list_size(patch_list);
+
+ /* Create the patch list. */
+ pr->patches = calloc(num_patches+1, sizeof(file *));
+ /* Set the last entry to NULL, to denote the end of the list. */
+ pr->patches[num_patches--] = NULL;
+ /* For each patch file in the list. */
+ for (linked_list *patch = patch_list; patch != NULL; patch = patch->prev, --num_patches) {
+ const char *patch_file = (const char *)patch->data;
+ /* Create a new entry in the list. */
+ pr->patches[num_patches] = create_file(root, patch_file);
+ }
+
+ cleanup_linked_list(patch_list);
+ }
+ }
+}
+
+static void parse_comment_reply(void *ctx, void *ret, const keyword *key, keyword_val val) {
+ linked_list *comment_list = *(linked_list **)ctx;
+ comment *comment = (comment *)ret;
+
+ /* Do we already have a reply? */
+ if (comment->reply != NULL) {
+ /* Check if this reply matches any other comment in the list. */
+ for (linked_list *node = get_tail(comment_list); node != NULL; node = node->prev) {
+ comment *reply = (comment *)node->data;
+
+ /* Do we have a match? */
+ if (comment->reply == reply) {
+ return;
+ }
+ }
+ }
+
+ /* Do we have a "reply-to" keyword? */
+ if (key->type == TYPE_INT) {
+ int found_reply = 0;
+ /* Check if that comment already exists in the list. */
+ for (linked_list *node = get_tail(comment_list); node != NULL; node = node->prev) {
+ comment *reply = (comment *)node->data;
+
+ /* Do we have a match? */
+ if (reply->id == val.i) {
+ /* Does this comment already have a reply? */
+ if (comment->reply != NULL) {
+ /* Free the reply. */
+ free(comment->reply);
+ }
+
+ /* Set our reply comment to the matched comment. */
+ comment->reply = reply;
+ found_reply = 1;
+ break;
+ }
+ }
+
+ /* Did we find a reply in the list? */
+ if (found_reply) {
+ return;
+ }
+ }
+
+ /* Do we not have a reply? */
+ if (comment->reply == NULL) {
+ /* Create one. */
+ comment->reply = calloc(1, sizeof(comment));
+ }
+ /* Which keyword do we have? */
+ switch (key->type) {
+ /* reply-to */
+ case TYPE_INT : comment->reply->id = val.i; break;
+ /* reply-author */
+ case TYPE_STRING: comment->reply->author = val.str; break;
+ /* reply-date */
+ case TYPE_TIME : comment->reply->date = val.t; break;
+ default : break;
+ }
+}
+
+typedef int (parse_callback)(void **ret, void *ctx, char *buf);
+
+static int parse_colon_key_value_file(void *ret, void *ctx, const keyword **keywords, char *buf, parse_callback *parse_cb) {
+ if (buf != NULL) {
+ int ret = 0;
+ for (;;) {
+ char *lhs, *rhs;
+ /* Find the keyword before the colon. */
+ lhs = strtok_r(skip_whitespace(buf), ":", &buf);
+ /* Remove any whitespace ahead of us. */
+ lhs = strtok_r(lhs, " \t\v\r\n", &rhs);
+
+ /* Did we hit EOF? */
+ if (lhs == NULL) {
+ break;
+ } else {
+ char *tmp = skip_whitespace(buf);
+
+ /* Does the right hand side start with a double, or single quote? */
+ if (*tmp == '\"' || *tmp == '\'') {
+ const char *delm = (delm == '\"') ? "\"" : "\'";
+ /* Get the string in between the start, and end qoute. */
+ rhs = get_str_delm_range(tmp, delm, delm, &buf);
+ } else {
+ /* Get the rest of the line. */
+ rhs = strtok_r(tmp, "\n", &buf);
+ }
+
+ /* Did we fail to parse the keyword? */
+ if (int error = parse_keywords(keywords, lhs, rhs, ret, ctx)) {
+ log(LOG_WARNING, "Failed to parse keyword \"%s\". Error code: %i", lhs, error);
+ ret = error;
+ }
+ }
+
+ /* Do we have a parse callback? */
+ if (parse_cb != NULL) {
+ /* Did the callback return an error. */
+ if (parse_cb(&ret, ctx, buf) < 0) {
+ ret = 5;
+ break;
+ }
+ }
+ }
+ return ret;
+ } else {
+ return 6;
+ }
+}
+
+static int is_end_of_comment(void **ret, void *ctx, char *buf) {
+ /* Do we have at least one blank line? */
+ if (strspn(buf, "\n") >= 1) {
+ linked_list **comment_list = (linked_list **)ctx;
+ if (comment_list != NULL) {
+ /* Is our comment list NULL, or is there an existing entry? */
+ if (*comment_list == NULL || (*comment_list)->data == NULL) {
+ add_node(comment_list, *ret);
+ }
+ *ret = calloc(1, sizeof(comment));
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int parse_comments_file(comment ***comments, char *buf) {
+ if (comments == NULL) {
+ return 7;
+ } else {
+ linked_list *comment_list = NULL;
+ int ret = parse_colon_key_value_file(calloc(1, sizeof(comment)), &comment_list, comment_keywords, buf, is_end_of_comment);
+ *comments = linked_list_to_array(comment_list);
+ cleanup_linked_list(comments_list);
+ return ret;
+ }
+}
+
+
+static int parse_info_file(pull_request *pr, char *buf, const char *root) {
+ return parse_colon_key_value_file(pr, root, info_keywords, buf, NULL);
+}
+
+static int parse_comments_file_path(comment ***comments, const char *root) {
+ file *f = create_file(root, "comments");
+ int ret = parse_comments_file(comments, f->buf);
+ free_file(f);
+ return ret;
+}
+
+static int parse_info_file_path(pull_request *pr, const char *root) {
+ file *f = create_file(root, "info");
+ int ret = parse_info_file(pr, f->buf, root);
+ free_file(f);
+ return ret;
+}
+
+pull_request *get_pull_request(index *idx, const char *root) {
+ /* Do we have a valid index? */
+ if (is_valid_index(idx)) {
+ char *pr_dir;
+ char *idx_str = index_to_str(idx);
+
+ /* Does the converted path of our index exist? */
+ if (index_path_exists(idx, root, &pr_dir) >= 0) {
+ pull_request *pr = calloc(1, sizeof(pull_request));
+
+ log(LOG_NOTICE, "Found PR #%s with path \"%s\".", idx_str, pr_dir);
+
+ /* Did we successfully parse the info file? */
+ if (parse_info_file_path(pr, pr_dir) <= 1) {
+ log(LOG_NOTICE, "Successfully parsed info file of PR #%s.", idx_str);
+
+ /* Did we successfully parse the comments file? */
+ if (parse_comments_file_path(&pr->comments, pr_dir) <= 1) {
+ log(LOG_NOTICE, "Successfully parsed comments file of PR #%s.", idx_str);
+ } else {
+ log(LOG_WARNING, "Failed to parse comments file of PR #%s.", idx_str);
+ }
+
+ return pr;
+ } else {
+ log(LOG_ERR, "Failed to parse info file of PR #%s.", idx_str);
+ free(pr);
+ return NULL;
+ }
+ } else {
+ log(LOG_ERR, "No PR #%s found.", idx_str);
+ free(idx_str);
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
}
int get_info_len(pull_request *pr) {
diff --git a/git.h b/git.h
index ef1b83a..29fec4b 100644
--- a/git.h
+++ b/git.h
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <time.h>
#include "index.h"
+#include "keyword.h"
typedef struct pull_request pull_request;
typedef struct file file;
@@ -48,4 +49,32 @@ extern git_repository **init_git(config *cfg);
extern int add_comment(comment *comment, const char *pr_root);
extern int create_pull_request_dir(pull_request *pr, index *idx, const char *root);
+void parse_patch_list(void *ctx, void *ret, const keyword *key, keyword_val val);
+void parse_comment_reply(void *ctx, void *ret, const keyword *key, keyword_val val);
+
+#define offset_list(...) (size_t []){__VA_ARGS__, -1}
+static const keyword *info_keywords[] = {
+ &(const keyword *){"title", "Title of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, title)), NULL},
+ &(const keyword *){"author", "Author of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, author)), NULL},
+ &(const keyword *){"date", "Date of Pull Request's creation.", "%a %b %e %H:%M:%S %Y %z", TYPE_TIME, offset_list(offsetof(pull_request, date)), NULL},
+ &(const keyword *){"description", "Description of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, desc)), NULL},
+ &(const keyword *){"type", "Type of Pull Request (0 = patch files, 1 = remote repo).", NULL, TYPE_INT, offset_list(offsetof(pull_request, pr_type)), NULL},
+ &(const keyword *){"branch", "Branch of remote repo.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, branch), offsetof(git_branch, name)), NULL},
+ &(const keyword *){"repo", "URL of remote repo.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, branch), offsetof(git_branch, repo)), NULL},
+ &(const keyword *){"patches", "Patch file(s) of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, patches), offsetof(file, name)), parse_patch_list},
+ NULL,
+};
+
+static const keyword *comment_keywords[] = {
+ &(const keyword *){"id", "ID of Comment.", NULL, TYPE_INT, offset_list(offsetof(comment, id)), NULL},
+ &(const keyword *){"author", "Author of Comment.", NULL, TYPE_STRING, offset_list(offsetof(comment, author)), NULL},
+ &(const keyword *){"date", "Date of Comment's creation.", "%a %b %e %H:%M:%S %Y %z", TYPE_TIME, offset_list(offsetof(comment, date)), NULL},
+ &(const keyword *){"reply-to", "ID of replied Comment.", NULL, TYPE_INT, offset_list(offsetof(comment, reply), offsetof(comment, id)), parse_comment_reply},
+ &(const keyword *){"reply-author", "Author of replied Comment.", NULL, TYPE_STRING, offset_list(offsetof(comment, reply), offsetof(comment, author)), parse_comment_reply},
+ &(const keyword *){"reply-date", "Date of replied Comment's creation", "%a %b %e %H:%M:%S %Y %z", TYPE_TIME, offset_list(offsetof(comment, reply), offsetof(comment, date)), parse_comment_reply},
+ &(const keyword *){"description", "Message of Comment.", NULL, TYPE_STRING, offset_list(offsetof(comment, desc)), NULL},
+ NULL,
+};
+#undef offset_list
+
#endif