744 lines
16 KiB
C
744 lines
16 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
|
* Please, refer LICENSE file for legal information
|
|
*
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* @file todo.c
|
|
* @author Ilja Kartašov <ik@lowenware.com>
|
|
* @brief AISL ToDo.txt module code file
|
|
*
|
|
* @see https://lowenware.com/
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <uuid.h>
|
|
#include <string.h>
|
|
#include <components/query.h>
|
|
#include <components/quick.h>
|
|
#include <components/log.h>
|
|
#include <cStuff/string.h>
|
|
#include <mods/minion.h>
|
|
|
|
|
|
typedef enum {
|
|
AX_MINION_UNDEFINED
|
|
, AX_MINION_TODAY
|
|
, AX_MINION_CREATE_TASK
|
|
, AX_MINION_READ_TASK
|
|
, AX_MINION_UPDATE_TASK
|
|
, AX_MINION_DELETE_TASK
|
|
} AxMinionMode;
|
|
|
|
|
|
struct context {
|
|
struct ax_context root;
|
|
struct ax_query qs;
|
|
struct ax_todo_task task;
|
|
|
|
AxMinionMode mode;
|
|
};
|
|
|
|
typedef struct context * Context;
|
|
|
|
|
|
typedef int
|
|
(*AxMinionCompare)(void *object1, void *object2);
|
|
|
|
static const char modName[] = "minion";
|
|
|
|
|
|
static Context
|
|
context_new(const char *path, AislHttpMethod method, AxMinion mod)
|
|
{
|
|
Context ctx;
|
|
|
|
if (!(ctx = (Context)ax_context_new(AX_MODULE(mod))))
|
|
return ctx;
|
|
|
|
if (strcmp(path, "today/") == 0)
|
|
ctx->mode = AX_MINION_TODAY;
|
|
else if (strcmp(path, "create/") == 0)
|
|
ctx->mode = AX_MINION_CREATE_TASK;
|
|
else if (strcmp(path, "read/") == 0)
|
|
ctx->mode = AX_MINION_READ_TASK;
|
|
else if (strcmp(path, "update/") == 0)
|
|
ctx->mode = AX_MINION_UPDATE_TASK;
|
|
else if (strcmp(path, "delete/") == 0)
|
|
ctx->mode = AX_MINION_DELETE_TASK;
|
|
|
|
ctx->task.priority = AX_TODO_ZERO_PRIORITY;
|
|
|
|
return ctx;
|
|
}
|
|
|
|
|
|
static void
|
|
context_free(Context ctx)
|
|
{
|
|
ax_query_release(&ctx->qs);
|
|
ax_todo_task_release(&ctx->task);
|
|
|
|
ax_context_free((AxContext)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)
|
|
{
|
|
Context ctx = (Context) p_ctx;
|
|
/* AxMinion mod = (AxMinion)((AxContext)ctx)->mod; */
|
|
char **out = NULL;
|
|
|
|
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 && !(*out)) {
|
|
if (cstuff_strncpy(out, val, v_len) == -1)
|
|
return -1;
|
|
ax_query__decode(*out);
|
|
ax_todo__remove_line_break(*out);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static AislStatus
|
|
on_stream_open(AxMinion mod, const struct aisl_evt_open *evt)
|
|
{
|
|
Context ctx;
|
|
AislStream s = (AislStream)evt->evt.source;
|
|
|
|
if (!(ctx = context_new(&evt->path[AX_MODULE(mod)->ep_length],
|
|
evt->http_method, mod))) {
|
|
return AISL_MALLOC_ERROR;
|
|
}
|
|
|
|
aisl_set_context(s, ctx);
|
|
|
|
return AISL_SUCCESS;
|
|
}
|
|
|
|
|
|
static AislStatus
|
|
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);
|
|
|
|
if(ax_query_init(&ctx->qs, total, on_input_var, (void*)ctx) != 0) {
|
|
ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR);
|
|
}
|
|
}
|
|
return AISL_SUCCESS;
|
|
}
|
|
|
|
|
|
static AislStatus
|
|
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;
|
|
}
|
|
|
|
|
|
static void
|
|
ax_minion_date2str(time_t tt, char *str_dt, size_t sz)
|
|
{
|
|
struct tm *p_tm = gmtime(&tt);
|
|
strftime(str_dt, sz, "%Y-%m-%d", p_tm);
|
|
}
|
|
|
|
|
|
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_read_task(AxMinion mod, AislStream s, Context ctx)
|
|
{
|
|
int i;
|
|
char str_buf[36 + 1];
|
|
CStuffList lst;
|
|
|
|
if (aisl_response(s, AISL_HTTP_OK, AISL_AUTO_LENGTH) != AISL_SUCCESS)
|
|
goto e_reject;
|
|
|
|
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;
|
|
|
|
if (i != 0 && aisl_write(s, ",", 1) == -1)
|
|
goto e_reject;
|
|
|
|
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->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->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, "\"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;
|
|
|
|
if (aisl_flush(s) != AISL_SUCCESS)
|
|
goto e_reject;
|
|
|
|
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)
|
|
{
|
|
Context ctx = aisl_get_context((AislStream)evt->source);
|
|
AislStream s = (AislStream)evt->source;
|
|
|
|
/* verify input */
|
|
AX_LOG_STATE("%s: request mode = %d", modName, ctx->mode);
|
|
switch(ctx->mode) {
|
|
case AX_MINION_TODAY:
|
|
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:
|
|
goto bad_request;
|
|
}
|
|
|
|
goto finally;
|
|
|
|
bad_request:
|
|
ax_quick_response(s, AISL_HTTP_BAD_REQUEST);
|
|
|
|
finally:
|
|
return AISL_SUCCESS;
|
|
}
|
|
|
|
|
|
static AislStatus
|
|
on_stream_close(AxMinion mod, const struct aisl_evt *evt)
|
|
{
|
|
context_free((Context)aisl_get_context((AislStream)evt->source));
|
|
return AISL_SUCCESS;
|
|
}
|
|
|
|
|
|
static AislStatus
|
|
ax_minion_on_event(AxMinion mod, const struct aisl_evt *evt)
|
|
{
|
|
switch(evt->code) {
|
|
case AISL_EVENT_STREAM_OPEN:
|
|
return on_stream_open(mod, (const struct aisl_evt_open *)evt);
|
|
|
|
case AISL_EVENT_STREAM_HEADER:
|
|
return on_stream_header(mod, (const struct aisl_evt_header *)evt);
|
|
|
|
case AISL_EVENT_STREAM_INPUT:
|
|
return on_stream_input(mod, (const struct aisl_evt_input *)evt);
|
|
|
|
case AISL_EVENT_STREAM_REQUEST:
|
|
return on_stream_request(mod, evt);
|
|
|
|
case AISL_EVENT_STREAM_CLOSE:
|
|
return on_stream_close(mod, evt);
|
|
|
|
default:
|
|
return AISL_IDLE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
AislStatus
|
|
ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg)
|
|
{
|
|
AislStatus result;
|
|
|
|
AX_MODULE_INIT(ax_minion, cfg->end_point);
|
|
|
|
if ((result = ax_todo_init(&mod->todo, cfg->todo_file)) != AISL_SUCCESS)
|
|
return result;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|