diff options
Diffstat (limited to 'src/model.c')
| -rw-r--r-- | src/model.c | 246 |
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; +} |