From b9e247b88f43dfeae69ac1371a78787950d859ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilja=20Karta=C5=A1ov?= Date: Fri, 28 Jun 2019 15:59:02 +0200 Subject: [PATCH] Add all core functions for minion module --- components/todo.c | 223 +++++++++++++++++---- components/todo.h | 57 +++++- mods/minion.c | 479 ++++++++++++++++++++++++++++++++++++++++++++-- mods/minion.h | 3 + mods/module.h | 2 +- 5 files changed, 693 insertions(+), 71 deletions(-) diff --git a/components/todo.c b/components/todo.c index eb31ecf..9a99b76 100644 --- a/components/todo.c +++ b/components/todo.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "todo.h" @@ -25,19 +26,12 @@ AislStatus ax_todo_init(AxTodo self, const char *file) { - FILE *f; - uuid_generate(self->id); + uuid_generate(self->list_id); - if (!(f = fopen(file, "a+"))) { - return AISL_SYSCALL_ERROR; - } + self->file = file; - if (cstuff_list_init(&self->list, 16) != CSTUFF_SUCCESS) { - fclose(f); + if (cstuff_sprintf(&self->swap, "%s.swap", file) == -1) return AISL_MALLOC_ERROR; - } - - self->fd = f; return AISL_SUCCESS; } @@ -46,32 +40,126 @@ ax_todo_init(AxTodo self, const char *file) void ax_todo_release(AxTodo self) { - if (self->fd) - fclose(self->fd); + if (self->swap) + free(self->swap); +} - cstuff_list_release(&self->list, (CStuffListFree)ax_todo_task_free); + +static AislStatus +ax_todo_add_project(CStuffList list, time_t completed, char *key, size_t k_len) +{ + int i; + AxTodoProject proj; + + for (i = 0; i < list->length; i++) { + proj = list->items[i]; + + if (strncmp(proj->name, key, k_len) == 0 && !proj->name[k_len]) { + proj->ref_count++; + + if (proj->last_completed < completed) { + proj->last_completed = completed; + } + return true; + } + } + + if (!(proj = calloc(1, sizeof (*proj)))) + return false; + + if (cstuff_strncpy(&proj->name, key, k_len) != -1) { + proj->ref_count = 1; + proj->last_completed = completed; + uuid_generate(proj->project_id); + if (cstuff_list_append(list, proj) != -1) + return true; + } + + ax_todo_project_free(proj); + return false; +} + + +static AislStatus +ax_todo_add_tag(CStuffList list, char *key, size_t k_len) +{ + int i; + AxTodoTag tag; + + for (i = 0; i < list->length; i++) { + tag = list->items[i]; + + if (strncmp(tag->name, key, k_len) == 0 && !tag->name[k_len]) { + tag->ref_count++; + return true; + } + } + + if (!(tag = calloc(1, sizeof (*tag)))) + return false; + + if (cstuff_strncpy(&tag->name, key, k_len) != -1) { + tag->ref_count = 1; + uuid_generate(tag->tag_id); + if (cstuff_list_append(list, tag) != -1) + return true; + } + + ax_todo_tag_free(tag); + return false; +} + + +static AislStatus +ax_todo_read_keys(AxTodoTask task, CStuffList projs, CStuffList tags) +{ + const char tokens[] = "+@"; + char *s = task->description; + size_t offset = 0; + + for (;;) { + char t; + offset = strcspn(s, tokens); + + s += offset; + + if ((t = *(s++)) == 0) + break; + + offset = strcspn(s, " "); + + if (offset) { + if ((t == '+' && !ax_todo_add_project(projs, task->completed, s, offset)) + || (t == '@' && !ax_todo_add_tag(tags, s, offset))) { + return AISL_MALLOC_ERROR; + } + s += offset; + } + } + + return AISL_SUCCESS; } AislStatus -ax_todo_read(AxTodo self) +ax_todo_read(AxTodo self, CStuffList tasks, CStuffList projs, CStuffList tags) { - FILE *f = self->fd; + FILE *f = NULL; size_t sz = 256; char *buf; ssize_t rs; AislStatus result = AISL_SUCCESS; - - if ((fseek(f, 0L, SEEK_SET) != 0)) - return AISL_SYSCALL_ERROR; if (!(buf = malloc(sz))) return AISL_MALLOC_ERROR; + if (!(f = fopen(self->file, "r"))) + goto e_syscall; + while ((rs = getdelim(&buf, &sz, '\n', f)) != -1) { AxTodoTask task; - if (!(task = ax_todo_task_new())) + if (!(task = ax_todo_task_new(self->list_id))) goto e_malloc; if (ax_todo_task_set(task, buf, rs) != 0) { @@ -79,10 +167,14 @@ ax_todo_read(AxTodo self) goto e_input; } - if (cstuff_list_append(&self->list, task) == -1) { + if (cstuff_list_append(tasks, task) == -1) { ax_todo_task_free(task); goto e_malloc; } + + if ((result = ax_todo_read_keys(task, projs, tags)) != AISL_SUCCESS) { + goto e_malloc; + } } switch(errno) { @@ -109,6 +201,8 @@ e_syscall: finally: free(buf); + if (f) + fclose(f); return result; } @@ -124,52 +218,60 @@ ax_todo__write_dt(FILE *f, time_t dt) AislStatus -ax_todo_write(AxTodo self) +ax_todo_write(AxTodo self, CStuffList list) { - FILE *f = self->fd; + FILE *f; int i; - if ((fseek(f, 0L, SEEK_SET) != 0)) + if (!(f = fopen(self->swap, "w"))) return AISL_SYSCALL_ERROR; - f = self->fd; + for (i = 0; i < list->length; i++) { + AxTodoTask task = (AxTodoTask) list->items[i]; - for (i = 0; i < self->list.length; i++) { - AxTodoTask task = (AxTodoTask) self->list.items[i]; + if (uuid_compare(task->list_id, self->list_id) != 0) + continue; if (task->is_done) { fwrite("x ", 1, 2, f); } - if (task->priority) { + if (task->priority && task->priority != AX_TODO_ZERO_PRIORITY) { fprintf(f, "(%c) ", task->priority); } if (task->is_done) { - if (task->completion) { - ax_todo__write_dt(f, task->completion); - if (task->creation) { - ax_todo__write_dt(f, task->creation); + if (task->completed) { + ax_todo__write_dt(f, task->completed); + if (task->created) { + ax_todo__write_dt(f, task->created); } } - } else if (task->creation) { - ax_todo__write_dt(f, task->creation); + } else if (task->created) { + ax_todo__write_dt(f, task->created); } fprintf(f, "%s\n", task->description); } - return (fflush(f) == 0) ? AISL_SUCCESS : AISL_SYSCALL_ERROR; + fclose(f); + + if (cstuff_file_move(self->swap, self->file) != 0) + return AISL_SYSCALL_ERROR; + + return AISL_SUCCESS; } AxTodoTask -ax_todo_task_new(void) +ax_todo_task_new(uuid_t list_id) { AxTodoTask result; - if ((result = calloc(1, sizeof (struct ax_todo_task))) != NULL) { + if ((result = calloc(1, sizeof (*result))) != NULL) { + uuid_copy(result->list_id, list_id); uuid_generate(result->task_id); + result->priority = AX_TODO_ZERO_PRIORITY; } return result; @@ -220,7 +322,7 @@ ax_todo__get_priority(char *buf, char *p_priority) } -static int +int ax_todo__get_dt(char *buf, time_t *p_dt) { struct tm stm = {0}; @@ -228,6 +330,8 @@ ax_todo__get_dt(char *buf, time_t *p_dt) const char fmt[] = "Y-M-DTH:m:s.uZB:b ", *f = fmt; char *ptr = NULL; + setenv("TZ", "UTC", 1); + for (result = 0; *f != 0; result++, f++) { switch(*f) { case 'Y': @@ -329,7 +433,6 @@ ax_todo__get_dt(char *buf, time_t *p_dt) stm.tm_min += (b_min * z_bias); } - setenv("TZ", "UTC", 1); *p_dt = mktime(&stm); unsetenv("TZ"); @@ -337,12 +440,33 @@ ax_todo__get_dt(char *buf, time_t *p_dt) } e_input: - AX_LOG_DEBUG("Parse error: %c(%d) at %d", *f, (int) (f - fmt), result); + /* AX_LOG_DEBUG("Parse error: %c(%d) at %d", *f, (int) (f - fmt), result); */ return 0; (void) usec; } + +void +ax_todo__remove_line_break(char *description) +{ + int move = 0; + char c; + + do { + c = *description; + + if (c == '\r' || c == '\n') { + move++; + continue; + } + + *(description - move) = c; + + } while (c != '\0'); +} + + AislStatus ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length) { @@ -362,11 +486,11 @@ ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length) buf += l; } - l = ax_todo__get_dt(buf, (is_done) ? &task->completion : &task->creation); + l = ax_todo__get_dt(buf, (is_done) ? &task->completed : &task->created); if (l != 0) { buf += l; - if (is_done && (l = ax_todo__get_dt(buf, &task->creation)) != 0) { + if (is_done && (l = ax_todo__get_dt(buf, &task->created)) != 0) { buf += l; } } @@ -381,3 +505,20 @@ ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length) return AISL_SUCCESS; } + +void +ax_todo_project_free(AxTodoProject project) +{ + if (project->name) + free(project->name); + free(project); +} + + +void +ax_todo_tag_free(AxTodoTag tag) +{ + if (tag->name) + free(tag->name); + free(tag); +} diff --git a/components/todo.h b/components/todo.h index aea58d8..9e4b096 100644 --- a/components/todo.h +++ b/components/todo.h @@ -22,28 +22,49 @@ #include #include +#define AX_TODO_ZERO_PRIORITY '_' struct ax_todo { - uuid_t id; - FILE *fd; - struct cstuff_list list; + uuid_t list_id; + const char *file; + char *swap; + /* todo: add file timestamp */ }; typedef struct ax_todo * AxTodo; struct ax_todo_task { + uuid_t list_id; uuid_t task_id; - char priority; - time_t completion; - time_t creation; + time_t completed; + time_t created; char *description; - bool is_done; + bool is_done; + char priority; }; typedef struct ax_todo_task * AxTodoTask; +struct ax_todo_project { + uuid_t project_id; + time_t last_completed; + char *name; + uint32_t ref_count; +}; + +typedef struct ax_todo_project * AxTodoProject; + + +struct ax_todo_tag { + uuid_t tag_id; + char *name; + uint32_t ref_count; +}; + +typedef struct ax_todo_tag * AxTodoTag; + AislStatus ax_todo_init(AxTodo self, const char *file); @@ -53,15 +74,15 @@ ax_todo_release(AxTodo self); AislStatus -ax_todo_read(AxTodo self); +ax_todo_read(AxTodo self, CStuffList tasks, CStuffList projs, CStuffList tags); AislStatus -ax_todo_write(AxTodo self); +ax_todo_write(AxTodo self, CStuffList list); AxTodoTask -ax_todo_task_new(void); +ax_todo_task_new(uuid_t list_id); void @@ -72,8 +93,24 @@ void ax_todo_task_free(AxTodoTask task); +int +ax_todo__get_dt(char *buf, time_t *p_dt); + + +void +ax_todo__remove_line_break(char *description); + + AislStatus ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length); +void +ax_todo_project_free(AxTodoProject project); + + +void +ax_todo_tag_free(AxTodoTag tag); + + #endif /* !TODO_H */ diff --git a/mods/minion.c b/mods/minion.c index f19de6c..30ec57a 100644 --- a/mods/minion.c +++ b/mods/minion.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -37,7 +38,6 @@ struct context { struct ax_context root; struct ax_query qs; struct ax_todo_task task; - uuid_t file_id; AxMinionMode mode; }; @@ -45,6 +45,9 @@ struct context { typedef struct context * Context; +typedef int +(*AxMinionCompare)(void *object1, void *object2); + static const char modName[] = "minion"; @@ -56,7 +59,7 @@ context_new(const char *path, AislHttpMethod method, AxMinion mod) if (!(ctx = (Context)ax_context_new(AX_MODULE(mod)))) return ctx; - if (!(*path)) + if (strcmp(path, "today/") == 0) ctx->mode = AX_MINION_TODAY; else if (strcmp(path, "create/") == 0) ctx->mode = AX_MINION_CREATE_TASK; @@ -67,6 +70,8 @@ context_new(const char *path, AislHttpMethod method, AxMinion mod) else if (strcmp(path, "delete/") == 0) ctx->mode = AX_MINION_DELETE_TASK; + ctx->task.priority = AX_TODO_ZERO_PRIORITY; + return ctx; } @@ -81,24 +86,151 @@ context_free(Context ctx) } +static int +ax_minion_compare_tasks(AxTodoTask task1, AxTodoTask task2) +{ + if (task1->is_done) { + if (task2->is_done) { + if (task1->completed > task2->completed) { + return -1; + } else if (task1->completed > task2->completed) { + return 1; + } + } else { + return 1; + } + } else { + if (task2->is_done) { + return -1; + } else { + if (task1->priority < task2->priority) { + return -1; + } else if (task1->priority == task2->priority) { + if (task1->created > task2->created) { + return -1; + } else if (task1->created < task2->created) { + return 1; + } + } else { + return 1; + } + } + } + return 0; +} + + +static int +ax_minion_compare_projects(AxTodoProject proj1, AxTodoProject proj2) +{ + if (proj1->last_completed < proj2->last_completed) { + return -1; + } else if (proj1->last_completed > proj2->last_completed) { + return 1; + } + + if (proj1->ref_count > proj2->ref_count) { + return -1; + } else if (proj1->ref_count < proj2->ref_count) { + return 1; + } + + return 0; +} + + +static int +ax_minion_compare_tags(AxTodoTag tag1, AxTodoTag tag2) +{ + if (tag1->ref_count > tag2->ref_count) { + return -1; + } else if (tag1->ref_count < tag2->ref_count) { + return 1; + } + + return 0; +} + + +static void +ax_minion_sort(CStuffList list, AxMinionCompare compare_objects) +{ + int l = list->length, ld = l - 1, i, j; + void **data = list->items, *swap; + + for (i = 0; i < l; i++) { + int c = i + 1; + for (j = ld; j >= c; j--) { + if (compare_objects((AxTodoTask)data[j], (AxTodoTask)data[j - 1]) == -1) { + swap = data[j]; + data[j] = data[j - 1]; + data[j - 1] = swap; + } + } + } +} + + +static void +on_input_dt(const char *val, uint32_t v_len, time_t *p_out) +{ + char str_dt[36 + 1]; + if (!(*p_out) && !(v_len < 10 || v_len > sizeof (str_dt) - 1)) { + strncpy(str_dt, val, v_len); + str_dt[v_len] = 0; + (void) ax_todo__get_dt(str_dt, p_out); + } +} + + +static void +on_input_uuid(const char *val, uint32_t v_len, uuid_t uuid_out) +{ + char str_uuid[36 + 1]; + + if (v_len == 36) { + strncpy(str_uuid, val, v_len); + str_uuid[v_len] = 0; + uuid_parse(str_uuid, uuid_out); + } +} + + static int on_input_var(const char *key, uint32_t k_len, const char *val, uint32_t v_len, - void *p_ctx ) + void *p_ctx) { Context ctx = (Context) p_ctx; /* AxMinion mod = (AxMinion)((AxContext)ctx)->mod; */ char **out = NULL; - if (!ctx->task.description && k_len == 11) { - if (strncmp(key, "description", k_len) == 0) { + if (k_len == 1) { + if (*key == 't') { out = &ctx->task.description; + } else if (*key == 'x') { + ctx->task.is_done = (v_len == 4 && strncmp(val, "true", 4) == 0); + } else if (*key == 'p') { + if (v_len == 1 && !((*val < 0x41 || *val > 0x5A))) { + ctx->task.priority = *val; + } + } + } else if (k_len == 2) { + if (strncmp(key, "id", 2) == 0) { + on_input_uuid(val, v_len, ctx->task.task_id); + } else if (strncmp(key, "li", 2) == 0) { + on_input_uuid(val, v_len, ctx->task.list_id); + } else if (strncmp(key, "co", 2) == 0) { + on_input_dt(val, v_len, &ctx->task.completed); + } else if (strncmp(key, "cr", 2) == 0) { + on_input_dt(val, v_len, &ctx->task.created); } } - if (out) { + if (out && !(*out)) { if (cstuff_strncpy(out, val, v_len) == -1) return -1; ax_query__decode(*out); + ax_todo__remove_line_break(*out); } return 0; } @@ -127,6 +259,8 @@ on_stream_header(AxMinion mod, const struct aisl_evt_header *evt) AislStream s = (AislStream)evt->evt.source; Context ctx = aisl_get_context(s); + AX_LOG_DEBUG("header: %s = %s", evt->key, evt->value); + if (strcmp(evt->key, "content-length") == 0) { size_t total = strtoll(evt->value, NULL, 10); @@ -143,6 +277,7 @@ on_stream_input(AxMinion mod, const struct aisl_evt_input *evt) { Context ctx = aisl_get_context((AislStream)evt->evt.source); + AX_LOG_DEBUG("input[%d]: %s", evt->size, evt->data); ax_query_feed(&ctx->qs, evt->data, evt->size); return AISL_SUCCESS; @@ -157,11 +292,83 @@ ax_minion_date2str(time_t tt, char *str_dt, size_t sz) } +static bool +ax_minion_today(AxMinion mod, AislStream s, Context ctx) +{ + /* sprint: [{task1}, {task2}] + */ + return true; +} + + +static bool +ax_minion_create_task(AxMinion mod, AislStream s, Context ctx) +{ + AxTodoTask task; + char str_uuid[36 + 1]; + + if (!(ctx->task.description)) { + AX_LOG_STATE("%s: !description", modName); + return false; + } + + if (uuid_compare(ctx->task.list_id, mod->todo.list_id) != 0) { + AX_LOG_STATE("%s: !list_id", modName); + return false; + } + + if (!(task = ax_todo_task_new(ctx->task.list_id))) + goto e_system; + + task->is_done = ctx->task.is_done; + task->priority = ctx->task.priority; + if (!(task->created = ctx->task.created)) + time(&task->created); + task->completed = ctx->task.completed; + task->description = ctx->task.description; + uuid_copy(task->list_id, ctx->task.list_id); + ctx->task.description = NULL; + + if (cstuff_list_append(&mod->tasks, task) == -1) { + ax_todo_task_free(task); + goto e_system; + } + + if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS) + goto e_system; + + if (aisl_header(s, "Content-Type", "text/json") == -1) + goto e_reject; + + uuid_unparse_lower(task->list_id, str_uuid); + + if (aisl_printf(s, "{\"id\": \"%s\"}", str_uuid) == -1) + goto e_reject; + + if (aisl_flush(s) != AISL_SUCCESS) + goto e_reject; + +goto finally; + +e_reject: + aisl_reject(s); + goto finally; + +e_system: + ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR); + goto finally; + +finally: + return true; +} + + static void -ax_minion_stream_today(AxMinion mod, AislStream s, Context ctx) +ax_minion_read_task(AxMinion mod, AislStream s, Context ctx) { int i; - CStuffList lst = &mod->todo.list; + char str_buf[36 + 1]; + CStuffList lst; if (aisl_response(s, AISL_HTTP_OK, AISL_AUTO_LENGTH) != AISL_SUCCESS) goto e_reject; @@ -169,41 +376,110 @@ ax_minion_stream_today(AxMinion mod, AislStream s, Context ctx) if (aisl_header(s, "Content-Type", "text/json") == -1) goto e_reject; + /* tasks */ if (aisl_write(s, "{\"tasks\":[", 10) == -1) goto e_reject; + lst = &mod->tasks; for (i = 0; i < lst->length; i++) { AxTodoTask task = lst->items[i]; int rc; - char dt[32]; if (i != 0 && aisl_write(s, ",", 1) == -1) goto e_reject; - if (aisl_printf(s, "{\"x\":%s,", task->is_done ? "true" : "false") == -1) + uuid_unparse_lower(task->task_id, str_buf); + + if (aisl_printf(s, "\n{\"id\":\"%s\",", str_buf) == -1) + goto e_reject; + + uuid_unparse_lower(task->list_id, str_buf); + if (aisl_printf(s, "\"li\":\"%s\",", str_buf) == -1) + goto e_reject; + + if (aisl_printf(s, "\"x\":%s,", task->is_done ? "true" : "false") == -1) goto e_reject; rc = aisl_printf(s, "\"p\":\"%c\",", task->priority ? task->priority : '0'); if (rc == -1) goto e_reject; - if (task->is_done && task->completion) { - ax_minion_date2str(task->completion, dt, sizeof(dt)); - if (aisl_printf(s, "\"co\":\"%s\",", dt) == -1) + if (task->is_done && task->completed) { + ax_minion_date2str(task->completed, str_buf, sizeof(str_buf)); + if (aisl_printf(s, "\"co\":\"%s\",", str_buf) == -1) goto e_reject; } - if (task->creation) { - ax_minion_date2str(task->creation, dt, sizeof(dt)); - if (aisl_printf(s, "\"cr\":\"%s\",", dt) == -1) + if (task->created) { + ax_minion_date2str(task->created, str_buf, sizeof(str_buf)); + if (aisl_printf(s, "\"cr\":\"%s\",", str_buf) == -1) goto e_reject; } - rc = aisl_printf(s, "\"d\":\"%s\"}", task->description); + rc = aisl_printf(s, "\"t\":\"%s\"}", task->description); if (rc == -1) goto e_reject; } + /* projects */ + if (aisl_write(s, "\n], \"projects\":[", 16) == -1) + goto e_reject; + + lst = &mod->projects; + for (i = 0; i < lst->length; i++) { + AxTodoProject proj = lst->items[i]; + int rc; + + if (i != 0 && aisl_write(s, ",", 1) == -1) + goto e_reject; + + uuid_unparse_lower(proj->project_id, str_buf); + + if (aisl_printf(s, "\n{\"id\":\"%s\",", str_buf) == -1) + goto e_reject; + + if (aisl_printf(s, "\"n\":\"%s\",", proj->name) == -1) + goto e_reject; + + rc = aisl_printf(s, "\"r\":%"PRIu32"", proj->ref_count); + if (rc == -1) + goto e_reject; + + if (proj->last_completed) { + ax_minion_date2str(proj->last_completed, str_buf, sizeof(str_buf)); + if (aisl_printf(s, ",\"lc\":\"%s\"", str_buf) == -1) + goto e_reject; + } + + if (aisl_write(s, "}", 1) == -1) + goto e_reject; + + } + + /* tags */ + if (aisl_write(s, "\n], \"tags\":[", 12) == -1) + goto e_reject; + + lst = &mod->tags; + for (i = 0; i < lst->length; i++) { + AxTodoTag tag = lst->items[i]; + int rc; + + if (i != 0 && aisl_write(s, ",", 1) == -1) + goto e_reject; + + uuid_unparse_lower(tag->tag_id, str_buf); + + if (aisl_printf(s, "\n{\"id\":\"%s\",", str_buf) == -1) + goto e_reject; + + if (aisl_printf(s, "\"n\":\"%s\",", tag->name) == -1) + goto e_reject; + + rc = aisl_printf(s, "\"r\":%"PRIu32"}", tag->ref_count); + if (rc == -1) + goto e_reject; + } if (aisl_write(s, "]}", 2) == -1) goto e_reject; @@ -213,10 +489,135 @@ ax_minion_stream_today(AxMinion mod, AislStream s, Context ctx) return; e_reject: + AX_LOG_DEBUG("%s: reject", modName); aisl_reject(s); } +static bool +ax_minion_update_task(AxMinion mod, AislStream s, Context ctx) +{ + AxTodoTask task = NULL; + char str_uuid[36 + 1]; + int i; + + if (!(ctx->task.description)) { + AX_LOG_STATE("%s: !description", modName); + return false; + } + + if (uuid_compare(ctx->task.list_id, mod->todo.list_id) != 0) { + AX_LOG_STATE("%s: !list_id", modName); + return false; + } + + for (i = 0; i < mod->tasks.length; i++) { + task = (AxTodoTask) mod->tasks.items[i]; + if (uuid_compare(task->task_id, ctx->task.task_id) == 0) + break; + task = NULL; + } + + if (!task) { + AX_LOG_STATE("%s: !task_id", modName); + return false; + } + + if (!(task->priority = ctx->task.priority)) + task->priority = AX_TODO_ZERO_PRIORITY; + task->created = ctx->task.created; + task->completed = ctx->task.completed; + + if ((task->is_done = ctx->task.is_done) && !task->completed) { + time(&task->completed); + } + + if (task->description) + free(task->description); + task->description = ctx->task.description; + ctx->task.description = NULL; + uuid_copy(task->list_id, ctx->task.list_id); + + if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS) + goto e_system; + + if (aisl_header(s, "Content-Type", "text/json") == -1) + goto e_reject; + + uuid_unparse_lower(task->list_id, str_uuid); + + if (aisl_printf(s, "{\"id\": \"%s\"}", str_uuid) == -1) + goto e_reject; + + if (aisl_flush(s) != AISL_SUCCESS) + goto e_reject; + +goto finally; + +e_reject: + aisl_reject(s); + goto finally; + +e_system: + ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR); + goto finally; + +finally: + return true; +} + + +static bool +ax_minion_delete_task(AxMinion mod, AislStream s, Context ctx) +{ + AxTodoTask task = NULL; + char str_uuid[36 + 1]; + int i; + + for (i = 0; i < mod->tasks.length; i++) { + task = (AxTodoTask) mod->tasks.items[i]; + if (uuid_compare(task->task_id, ctx->task.task_id) == 0) { + cstuff_list_remove(&mod->tasks, i); + break; + } + task = NULL; + } + + if (!task) { + AX_LOG_STATE("%s: !task_id", modName); + return false; + } + + uuid_unparse_lower(task->list_id, str_uuid); + ax_todo_task_free(task); + + if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS) + goto e_system; + + if (aisl_header(s, "Content-Type", "text/json") == -1) + goto e_reject; + + if (aisl_printf(s, "{\"id\": \"%s\"}", str_uuid) == -1) + goto e_reject; + + if (aisl_flush(s) != AISL_SUCCESS) + goto e_reject; + +goto finally; + +e_reject: + aisl_reject(s); + goto finally; + +e_system: + ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR); + goto finally; + +finally: + return true; +} + + static AislStatus on_stream_request(AxMinion mod, const struct aisl_evt *evt) { @@ -227,7 +628,27 @@ on_stream_request(AxMinion mod, const struct aisl_evt *evt) AX_LOG_STATE("%s: request mode = %d", modName, ctx->mode); switch(ctx->mode) { case AX_MINION_TODAY: - ax_minion_stream_today(mod, s, ctx); + if (!ax_minion_today(mod, s, ctx)) + goto bad_request; + break; + + case AX_MINION_CREATE_TASK: + if (!ax_minion_create_task(mod, s, ctx)) + goto bad_request; + break; + + case AX_MINION_READ_TASK: + ax_minion_read_task(mod, s, ctx); + break; + + case AX_MINION_UPDATE_TASK: + if (!ax_minion_update_task(mod, s, ctx)) + goto bad_request; + break; + + case AX_MINION_DELETE_TASK: + if (!ax_minion_delete_task(mod, s, ctx)) + goto bad_request; break; default: @@ -288,9 +709,26 @@ ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg) if ((result = ax_todo_init(&mod->todo, cfg->todo_file)) != AISL_SUCCESS) return result; - if ((result = ax_todo_read(&mod->todo)) != AISL_SUCCESS) + if ((cstuff_list_init(&mod->tasks, 32)) != CSTUFF_SUCCESS) + return AISL_MALLOC_ERROR; + + if ((cstuff_list_init(&mod->projects, 8)) != CSTUFF_SUCCESS) + return AISL_MALLOC_ERROR; + + if ((cstuff_list_init(&mod->tags, 32)) != CSTUFF_SUCCESS) + return AISL_MALLOC_ERROR; + + if ((result = ax_todo_read(&mod->todo, &mod->tasks, &mod->projects, + &mod->tags)) != AISL_SUCCESS) return result; + ax_minion_sort(&mod->tasks, (AxMinionCompare)ax_minion_compare_tasks); + ax_minion_sort(&mod->projects, (AxMinionCompare)ax_minion_compare_projects); + ax_minion_sort(&mod->tags, (AxMinionCompare)ax_minion_compare_tags); + + if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS) { + AX_LOG_DEBUG("Write failed ;("); + } return result; } @@ -299,4 +737,7 @@ void ax_minion_release(AxMinion mod) { ax_todo_release(&mod->todo); + cstuff_list_release(&mod->tasks, (CStuffListFree) ax_todo_task_free); + cstuff_list_release(&mod->projects, (CStuffListFree) ax_todo_project_free); + cstuff_list_release(&mod->tags, (CStuffListFree) ax_todo_tag_free); } diff --git a/mods/minion.h b/mods/minion.h index f5df019..5f6047c 100644 --- a/mods/minion.h +++ b/mods/minion.h @@ -36,6 +36,9 @@ struct ax_minion_cfg { struct ax_minion { struct ax_module root; struct ax_todo todo; + struct cstuff_list tasks; + struct cstuff_list projects; + struct cstuff_list tags; }; diff --git a/mods/module.h b/mods/module.h index 965b0be..c8fca2b 100644 --- a/mods/module.h +++ b/mods/module.h @@ -46,7 +46,7 @@ struct ax_module { const char *end_point; /**< Root mod's URL or NULL */ AxObserver on_event; /**< Mod's stream event observer */ size_t ctx_size; /**< Mod's context size */ - uint8_t ep_length; /**< End-Point length */ + uint32_t ep_length; /**< End-Point length */ };