summaryrefslogtreecommitdiffstats
path: root/src/model.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/model.c')
-rw-r--r--src/model.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/model.c b/src/model.c
new file mode 100644
index 0000000..75ed0d4
--- /dev/null
+++ b/src/model.c
@@ -0,0 +1,246 @@
+#include "internal.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+ const char *name;
+ size_t offset;
+} address_field_t;
+
+static const address_field_t ADDRESS_FIELDS[] = {
+ {"cep", offsetof(cepis_address_t, cep)},
+ {"logradouro", offsetof(cepis_address_t, logradouro)},
+ {"complemento", offsetof(cepis_address_t, complemento)},
+ {"unidade", offsetof(cepis_address_t, unidade)},
+ {"bairro", offsetof(cepis_address_t, bairro)},
+ {"localidade", offsetof(cepis_address_t, localidade)},
+ {"uf", offsetof(cepis_address_t, uf)},
+ {"estado", offsetof(cepis_address_t, estado)},
+ {"regiao", offsetof(cepis_address_t, regiao)},
+ {"ibge", offsetof(cepis_address_t, ibge)},
+ {"gia", offsetof(cepis_address_t, gia)},
+ {"ddd", offsetof(cepis_address_t, ddd)},
+ {"siafi", offsetof(cepis_address_t, siafi)},
+};
+
+#define ADDRESS_FIELD_COUNT (sizeof(ADDRESS_FIELDS) / sizeof(ADDRESS_FIELDS[0]))
+
+static char **field_slot(cepis_address_t *address, size_t index)
+{
+ return (char **)((char *)address + ADDRESS_FIELDS[index].offset);
+}
+
+static cepis_address_t *address_new(void)
+{
+ cepis_address_t *address = calloc(1, sizeof(*address));
+ size_t i;
+
+ if (address == NULL)
+ return NULL;
+ for (i = 0; i < ADDRESS_FIELD_COUNT; i++) {
+ char **slot = field_slot(address, i);
+ *slot = cepis_strdup("");
+ if (*slot == NULL) {
+ cepis_address_free(address);
+ return NULL;
+ }
+ }
+ return address;
+}
+
+static bool set_field(cepis_address_t *address, size_t index, const char *value)
+{
+ char **slot = field_slot(address, index);
+ char *copy = cepis_strdup(value);
+
+ if (copy == NULL)
+ return false;
+ free(*slot);
+ *slot = copy;
+ return true;
+}
+
+void cepis_address_free(cepis_address_t *address)
+{
+ size_t i;
+
+ if (address == NULL)
+ return;
+ for (i = 0; i < ADDRESS_FIELD_COUNT; i++)
+ free(*field_slot(address, i));
+ if (address->geo != NULL) {
+ free(address->geo->precisao);
+ free(address->geo);
+ }
+ free(address->raw);
+ free(address);
+}
+
+void cepis_address_list_free(cepis_address_list_t *list)
+{
+ size_t i;
+
+ if (list == NULL)
+ return;
+ for (i = 0; i < list->count; i++)
+ cepis_address_free(list->items[i]);
+ free(list->items);
+ list->items = NULL;
+ list->count = 0;
+}
+
+void cepis_health_free(cepis_health_t *health)
+{
+ if (health == NULL)
+ return;
+ free(health->status);
+ free(health->dne);
+ free(health->base_gerada_em);
+ free(health);
+}
+
+bool cepis_geo_is_precise(const cepis_geo_t *geo)
+{
+ return geo != NULL && geo->precisao != NULL &&
+ strcmp(geo->precisao, "logradouro") == 0;
+}
+
+bool cepis_health_ok(const cepis_health_t *health)
+{
+ return health != NULL && health->status != NULL &&
+ strcmp(health->status, "ok") == 0;
+}
+
+static cepis_geo_t *geo_from_json(const json_value_t *block)
+{
+ const char *precisao;
+ cepis_geo_t *geo;
+ double lat;
+ double lon;
+ long long raio;
+
+ if (block == NULL || block->type != JSON_OBJECT)
+ return NULL;
+ if (!json_as_double(json_object_get(block, "lat"), &lat))
+ return NULL;
+ if (!json_as_double(json_object_get(block, "lon"), &lon))
+ return NULL;
+ if (!json_as_llong(json_object_get(block, "raio_m"), &raio))
+ return NULL;
+ precisao = json_as_string(json_object_get(block, "precisao"));
+ if (precisao == NULL)
+ return NULL;
+
+ geo = calloc(1, sizeof(*geo));
+ if (geo == NULL)
+ return NULL;
+ geo->lat = lat;
+ geo->lon = lon;
+ geo->raio_m = (long)raio;
+ geo->precisao = cepis_strdup(precisao);
+ if (geo->precisao == NULL) {
+ free(geo);
+ return NULL;
+ }
+ return geo;
+}
+
+cepis_address_t *cepis_address_from_json(const json_value_t *object,
+ const char *raw, size_t raw_len)
+{
+ cepis_address_t *address;
+ size_t i;
+
+ if (object == NULL || object->type != JSON_OBJECT)
+ return NULL;
+ address = address_new();
+ if (address == NULL)
+ return NULL;
+
+ for (i = 0; i < ADDRESS_FIELD_COUNT; i++) {
+ const char *value = json_as_string(
+ json_object_get(object, ADDRESS_FIELDS[i].name));
+ if (value == NULL)
+ continue;
+ if (!set_field(address, i, value)) {
+ cepis_address_free(address);
+ return NULL;
+ }
+ }
+
+ address->geo = geo_from_json(json_object_get(object, "geo"));
+
+ if (raw != NULL) {
+ address->raw = malloc(raw_len + 1);
+ if (address->raw == NULL) {
+ cepis_address_free(address);
+ return NULL;
+ }
+ memcpy(address->raw, raw, raw_len);
+ address->raw[raw_len] = '\0';
+ }
+ return address;
+}
+
+cepis_address_t *cepis_address_from_csv(const csv_row_t *header,
+ const csv_row_t *record)
+{
+ cepis_address_t *address;
+ size_t column;
+
+ if (header == NULL || record == NULL)
+ return NULL;
+ address = address_new();
+ if (address == NULL)
+ return NULL;
+
+ for (column = 0; column < header->count; column++) {
+ size_t i;
+ if (column >= record->count)
+ break;
+ for (i = 0; i < ADDRESS_FIELD_COUNT; i++) {
+ if (strcmp(header->fields[column],
+ ADDRESS_FIELDS[i].name) != 0)
+ continue;
+ if (!set_field(address, i, record->fields[column])) {
+ cepis_address_free(address);
+ return NULL;
+ }
+ break;
+ }
+ }
+ return address;
+}
+
+cepis_health_t *cepis_health_from_json(const json_value_t *object)
+{
+ cepis_health_t *health;
+ const char *text;
+
+ if (object == NULL || object->type != JSON_OBJECT)
+ return NULL;
+ health = calloc(1, sizeof(*health));
+ if (health == NULL)
+ return NULL;
+
+ text = json_as_string(json_object_get(object, "status"));
+ health->status = cepis_strdup(text);
+ text = json_as_string(json_object_get(object, "dne"));
+ health->dne = cepis_strdup(text);
+ text = json_as_string(json_object_get(object, "base_gerada_em"));
+ health->base_gerada_em = cepis_strdup(text);
+
+ if (health->status == NULL || health->dne == NULL ||
+ health->base_gerada_em == NULL) {
+ cepis_health_free(health);
+ return NULL;
+ }
+
+ if (!json_as_llong(json_object_get(object, "ceps"), &health->ceps))
+ health->ceps = 0;
+ if (!json_as_llong(json_object_get(object, "geo"), &health->geo))
+ health->geo = 0;
+ return health;
+}