Add minion module and todo component
This commit is contained in:
parent
c9c9bbbff6
commit
45b4f6bee0
|
@ -0,0 +1,383 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <uuid.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <cStuff/string.h>
|
||||||
|
#include <components/log.h>
|
||||||
|
#include "todo.h"
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_init(AxTodo self, const char *file)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
uuid_generate(self->id);
|
||||||
|
|
||||||
|
if (!(f = fopen(file, "a+"))) {
|
||||||
|
return AISL_SYSCALL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cstuff_list_init(&self->list, 16) != CSTUFF_SUCCESS) {
|
||||||
|
fclose(f);
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->fd = f;
|
||||||
|
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_todo_release(AxTodo self)
|
||||||
|
{
|
||||||
|
if (self->fd)
|
||||||
|
fclose(self->fd);
|
||||||
|
|
||||||
|
cstuff_list_release(&self->list, (CStuffListFree)ax_todo_task_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_read(AxTodo self)
|
||||||
|
{
|
||||||
|
FILE *f = self->fd;
|
||||||
|
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;
|
||||||
|
|
||||||
|
while ((rs = getdelim(&buf, &sz, '\n', f)) != -1) {
|
||||||
|
AxTodoTask task;
|
||||||
|
|
||||||
|
if (!(task = ax_todo_task_new()))
|
||||||
|
goto e_malloc;
|
||||||
|
|
||||||
|
if (ax_todo_task_set(task, buf, rs) != 0) {
|
||||||
|
ax_todo_task_free(task);
|
||||||
|
goto e_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cstuff_list_append(&self->list, task) == -1) {
|
||||||
|
ax_todo_task_free(task);
|
||||||
|
goto e_malloc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(errno) {
|
||||||
|
case 0:
|
||||||
|
goto finally;
|
||||||
|
|
||||||
|
case ENOMEM:
|
||||||
|
goto e_malloc;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto e_syscall;
|
||||||
|
}
|
||||||
|
|
||||||
|
e_input:
|
||||||
|
result = AISL_INPUT_ERROR;
|
||||||
|
goto finally;
|
||||||
|
|
||||||
|
e_malloc:
|
||||||
|
result = AISL_MALLOC_ERROR;
|
||||||
|
goto finally;
|
||||||
|
|
||||||
|
e_syscall:
|
||||||
|
result = AISL_SYSCALL_ERROR;
|
||||||
|
|
||||||
|
finally:
|
||||||
|
free(buf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ax_todo__write_dt(FILE *f, time_t dt)
|
||||||
|
{
|
||||||
|
char str_dt[16];
|
||||||
|
struct tm *p_stm = gmtime(&dt);
|
||||||
|
(void) strftime(str_dt, sizeof (str_dt), "%Y-%m-%d", p_stm);
|
||||||
|
fprintf(f, "%s ", str_dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_write(AxTodo self)
|
||||||
|
{
|
||||||
|
FILE *f = self->fd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if ((fseek(f, 0L, SEEK_SET) != 0))
|
||||||
|
return AISL_SYSCALL_ERROR;
|
||||||
|
|
||||||
|
f = self->fd;
|
||||||
|
|
||||||
|
for (i = 0; i < self->list.length; i++) {
|
||||||
|
AxTodoTask task = (AxTodoTask) self->list.items[i];
|
||||||
|
|
||||||
|
if (task->is_done) {
|
||||||
|
fwrite("x ", 1, 2, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (task->creation) {
|
||||||
|
ax_todo__write_dt(f, task->creation);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "%s\n", task->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (fflush(f) == 0) ? AISL_SUCCESS : AISL_SYSCALL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AxTodoTask
|
||||||
|
ax_todo_task_new(void)
|
||||||
|
{
|
||||||
|
AxTodoTask result;
|
||||||
|
|
||||||
|
if ((result = calloc(1, sizeof (struct ax_todo_task))) != NULL) {
|
||||||
|
uuid_generate(result->task_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_todo_task_release(AxTodoTask task)
|
||||||
|
{
|
||||||
|
if (task->description)
|
||||||
|
free(task->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_todo_task_free(AxTodoTask task)
|
||||||
|
{
|
||||||
|
ax_todo_task_release(task);
|
||||||
|
free(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ax_todo__get_priority(char *buf, char *p_priority)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
char priority = 0;
|
||||||
|
const char fmt[] = {'(', 'A', ')', ' '};
|
||||||
|
|
||||||
|
for (result = 0; result < 4; result++) {
|
||||||
|
switch (result) {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
if (buf[result] == fmt[result])
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
priority = buf[result];
|
||||||
|
if (!(priority < 0x41 || priority > 0x5A))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*p_priority = priority;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ax_todo__get_dt(char *buf, time_t *p_dt)
|
||||||
|
{
|
||||||
|
struct tm stm = {0};
|
||||||
|
int result, *p_num = NULL, b_hour = 0, b_min = 0, usec = 0, z_bias = 0;
|
||||||
|
const char fmt[] = "Y-M-DTH:m:s.uZB:b ", *f = fmt;
|
||||||
|
char *ptr = NULL;
|
||||||
|
|
||||||
|
for (result = 0; *f != 0; result++, f++) {
|
||||||
|
switch(*f) {
|
||||||
|
case 'Y':
|
||||||
|
p_num = &stm.tm_year;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
p_num = &stm.tm_mon;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
p_num = &stm.tm_mday;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
p_num = &stm.tm_hour;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
p_num = &stm.tm_min;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
p_num = &stm.tm_sec;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
p_num = &usec;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
p_num = &b_hour;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
p_num = &b_min;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
if (buf[result] != *f) {
|
||||||
|
f++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
if (buf[result] == *f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (buf[result] == ' ') {
|
||||||
|
f = &fmt[17];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
goto e_input;
|
||||||
|
|
||||||
|
case 'Z':
|
||||||
|
if (buf[result] == *f) {
|
||||||
|
f += 3;
|
||||||
|
z_bias = 'Z';
|
||||||
|
} else if (buf[result] == '-') {
|
||||||
|
z_bias = -1;
|
||||||
|
} else if (buf[result] == '+') {
|
||||||
|
z_bias = 1;
|
||||||
|
} else {
|
||||||
|
goto e_input;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
case ':':
|
||||||
|
case ' ':
|
||||||
|
if (buf[result] == *f)
|
||||||
|
continue;
|
||||||
|
/* go through */
|
||||||
|
default:
|
||||||
|
goto e_input;
|
||||||
|
} /* switch */
|
||||||
|
|
||||||
|
*p_num = strtol(&buf[result], &ptr, 10);
|
||||||
|
|
||||||
|
if (!ptr || ptr == &buf[result]) {
|
||||||
|
if (f > &fmt[6] && buf[result] == ' ') {
|
||||||
|
f = &fmt[17];
|
||||||
|
/* skip optional part of timestamp */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
goto e_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += (ptr - &buf[result] - 1);
|
||||||
|
} /* for */
|
||||||
|
|
||||||
|
/* validate results */
|
||||||
|
/* ToDo: improve (stm.tm_mday < 31) check */
|
||||||
|
if (!(stm.tm_year < 1900) && (stm.tm_mon) && (stm.tm_mday) &&
|
||||||
|
(stm.tm_mon < 13) && (stm.tm_mday < 31) && (stm.tm_hour < 24) &&
|
||||||
|
(stm.tm_min < 60) && (stm.tm_sec < 60)) {
|
||||||
|
stm.tm_year -= 1900;
|
||||||
|
stm.tm_mon--;
|
||||||
|
|
||||||
|
if (z_bias == 0) { /* local time zone */
|
||||||
|
*p_dt = mktime(&stm);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z_bias != 'Z') {
|
||||||
|
stm.tm_hour += (b_hour * z_bias);
|
||||||
|
stm.tm_min += (b_min * z_bias);
|
||||||
|
}
|
||||||
|
|
||||||
|
setenv("TZ", "UTC", 1);
|
||||||
|
*p_dt = mktime(&stm);
|
||||||
|
unsetenv("TZ");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
e_input:
|
||||||
|
AX_LOG_DEBUG("Parse error: %c(%d) at %d", *f, (int) (f - fmt), result);
|
||||||
|
return 0;
|
||||||
|
(void) usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length)
|
||||||
|
{
|
||||||
|
bool is_done;
|
||||||
|
int l;
|
||||||
|
|
||||||
|
if (strncmp(buf, "x ", 2) == 0) {
|
||||||
|
is_done = true;
|
||||||
|
buf += 2;
|
||||||
|
} else {
|
||||||
|
is_done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
task->is_done = is_done;
|
||||||
|
|
||||||
|
if ((l = ax_todo__get_priority(buf, &task->priority)) != 0) {
|
||||||
|
buf += l;
|
||||||
|
}
|
||||||
|
|
||||||
|
l = ax_todo__get_dt(buf, (is_done) ? &task->completion : &task->creation);
|
||||||
|
|
||||||
|
if (l != 0) {
|
||||||
|
buf += l;
|
||||||
|
if (is_done && (l = ax_todo__get_dt(buf, &task->creation)) != 0) {
|
||||||
|
buf += l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((l = cstuff_strcpy(&task->description, buf)) == -1) {
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l > 1 && task->description[l-1] == '\n')
|
||||||
|
task->description[l-1] = 0;
|
||||||
|
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file todo.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief ToDo.txt module
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TODO_H_FAD28DF0_DA05_4436_88E5_E3715DA254E8
|
||||||
|
#define TODO_H_FAD28DF0_DA05_4436_88E5_E3715DA254E8
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <aisl/aisl.h>
|
||||||
|
#include <cStuff/list.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct ax_todo {
|
||||||
|
uuid_t id;
|
||||||
|
FILE *fd;
|
||||||
|
struct cstuff_list list;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ax_todo * AxTodo;
|
||||||
|
|
||||||
|
|
||||||
|
struct ax_todo_task {
|
||||||
|
uuid_t task_id;
|
||||||
|
char priority;
|
||||||
|
time_t completion;
|
||||||
|
time_t creation;
|
||||||
|
char *description;
|
||||||
|
bool is_done;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ax_todo_task * AxTodoTask;
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_init(AxTodo self, const char *file);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_todo_release(AxTodo self);
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_read(AxTodo self);
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_write(AxTodo self);
|
||||||
|
|
||||||
|
|
||||||
|
AxTodoTask
|
||||||
|
ax_todo_task_new(void);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_todo_task_release(AxTodoTask task);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_todo_task_free(AxTodoTask task);
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !TODO_H */
|
|
@ -0,0 +1,302 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* 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 <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;
|
||||||
|
uuid_t file_id;
|
||||||
|
|
||||||
|
AxMinionMode mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct context * Context;
|
||||||
|
|
||||||
|
|
||||||
|
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 (!(*path))
|
||||||
|
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;
|
||||||
|
|
||||||
|
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
|
||||||
|
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 (!ctx->task.description && k_len == 11) {
|
||||||
|
if (strncmp(key, "description", k_len) == 0) {
|
||||||
|
out = &ctx->task.description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out) {
|
||||||
|
if (cstuff_strncpy(out, val, v_len) == -1)
|
||||||
|
return -1;
|
||||||
|
ax_query__decode(*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);
|
||||||
|
|
||||||
|
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_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 void
|
||||||
|
ax_minion_stream_today(AxMinion mod, AislStream s, Context ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
CStuffList lst = &mod->todo.list;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (aisl_write(s, "{\"tasks\":[", 10) == -1)
|
||||||
|
goto e_reject;
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
goto e_reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->creation) {
|
||||||
|
ax_minion_date2str(task->creation, dt, sizeof(dt));
|
||||||
|
if (aisl_printf(s, "\"cr\":\"%s\",", dt) == -1)
|
||||||
|
goto e_reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = aisl_printf(s, "\"d\":\"%s\"}", task->description);
|
||||||
|
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:
|
||||||
|
aisl_reject(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
ax_minion_stream_today(mod, s, ctx);
|
||||||
|
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 ((result = ax_todo_read(&mod->todo)) != AISL_SUCCESS)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_minion_release(AxMinion mod)
|
||||||
|
{
|
||||||
|
ax_todo_release(&mod->todo);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file mod-feedback.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief AISL ToDo.txt module header file
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MINION_H2FC5C912_0F59_43AD_B805_B0F7C82BE2EF
|
||||||
|
#define MINION_H2FC5C912_0F59_43AD_B805_B0F7C82BE2EF
|
||||||
|
|
||||||
|
#include <cStuff/list.h>
|
||||||
|
#include <mods/context.h>
|
||||||
|
#include <mods/module.h>
|
||||||
|
#include <components/todo.h>
|
||||||
|
|
||||||
|
/* ToDo:
|
||||||
|
* Start with one todo.txt file
|
||||||
|
* Write ToDo.txt parser
|
||||||
|
* Join them together
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ax_minion_cfg {
|
||||||
|
const char *end_point;
|
||||||
|
const char *todo_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct ax_minion {
|
||||||
|
struct ax_module root;
|
||||||
|
struct ax_todo todo;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct ax_minion *AxMinion;
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ax_minion_release(AxMinion mod);
|
||||||
|
|
||||||
|
#endif /* !MINION_H */
|
Loading…
Reference in New Issue