From e05f02acd583797ceb449fc501d371d45a4293c1 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Mon, 17 Jul 2023 23:03:59 +0200 Subject: switch to JSON-RPC as message format Instead of the weird `struct msg` I had, I switched to the JSON-RPC format. It's basically the same, but has a well-defined semantics in case of errors. --- src/json.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 src/json.c (limited to 'src/json.c') diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..a01aae6 --- /dev/null +++ b/src/json.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2023 Egor Tensin + * This file is part of the "cimple" project. + * For details, see https://github.com/egor-tensin/cimple. + * Distributed under the MIT License. + */ + +#include "json.h" +#include "log.h" +#include "net.h" + +#include +#include + +#include +#include +#include +#include + +char *json_to_string(struct json_object *obj) +{ + const char *result = json_object_to_json_string(obj); + if (!result) { + json_errno("json_object_to_json_string"); + return NULL; + } + + char *_result = strdup(result); + if (!_result) { + log_errno("strdup"); + return NULL; + } + + return _result; +} + +struct json_object *json_from_string(const char *src) +{ + enum json_tokener_error error; + + struct json_object *result = json_tokener_parse_verbose(src, &error); + if (!result) { + json_errno("json_tokener_parse_verbose"); + log_err("JSON: parsing failed: %s\n", json_tokener_error_desc(error)); + return NULL; + } + + return result; +} + +int json_clone(const struct json_object *obj, const char *key, struct json_object **_value) +{ + int ret = 0; + + struct json_object *old_value = NULL; + ret = json_get(obj, key, &old_value); + if (ret < 0) + return ret; + + struct json_object *new_value = NULL; + ret = json_object_deep_copy(old_value, &new_value, NULL); + if (ret < 0) + return ret; + + *_value = new_value; + return ret; +} + +int json_send(struct json_object *obj, int fd) +{ + int ret = 0; + + const char *str = json_to_string(obj); + if (!str) + return -1; + + struct buf *buf = NULL; + ret = buf_pack_strings(&buf, 1, &str); + free((char *)str); + if (ret < 0) + return ret; + + ret = net_send_buf(fd, buf); + buf_destroy(buf); + if (ret < 0) + return ret; + + return ret; +} + +struct json_object *json_recv(int fd) +{ + struct json_object *result = NULL; + int ret = 0; + + struct buf *buf = NULL; + ret = net_recv_buf(fd, &buf); + if (ret < 0) + return NULL; + + result = json_from_string((const char *)buf_get_data(buf)); + if (!result) + goto destroy_buf; + +destroy_buf: + buf_destroy(buf); + + return result; +} + +int json_has(const struct json_object *obj, const char *key) +{ + return json_object_object_get_ex(obj, key, NULL); +} + +int json_get(const struct json_object *obj, const char *key, struct json_object **value) +{ + if (!json_has(obj, key)) { + log_err("JSON: key is missing: %s\n", key); + return -1; + } + + return json_object_object_get_ex(obj, key, value); +} + +int json_get_string(const struct json_object *obj, const char *key, const char **_value) +{ + struct json_object *value = NULL; + + int ret = json_get(obj, key, &value); + if (ret < 0) + return ret; + + if (!json_object_is_type(value, json_type_string)) { + log_err("JSON: key is not a string: %s\n", key); + return -1; + } + + *_value = json_object_get_string(value); + return 0; +} + +int json_get_int(const struct json_object *obj, const char *key, int64_t *_value) +{ + struct json_object *value = NULL; + + int ret = json_get(obj, key, &value); + if (ret < 0) + return ret; + + if (!json_object_is_type(value, json_type_int)) { + log_err("JSON: key is not an integer: %s\n", key); + return -1; + } + + errno = 0; + int64_t tmp = json_object_get_int64(value); + if (errno) { + log_err("JSON: failed to parse integer from key: %s\n", key); + return -1; + } + + *_value = tmp; + return 0; +} + +static int json_set_internal(struct json_object *obj, const char *key, struct json_object *value, + unsigned flags) +{ + int ret = 0; + + ret = json_object_object_add_ex(obj, key, value, flags); + if (ret < 0) { + json_errno("json_object_object_add_ex"); + return ret; + } + + return 0; +} + +static int json_set_string_internal(struct json_object *obj, const char *key, const char *_value, + unsigned flags) +{ + struct json_object *value = json_object_new_string(_value); + if (!value) { + json_errno("json_object_new_string"); + return -1; + } + + int ret = json_set_internal(obj, key, value, flags); + if (ret < 0) + goto free_value; + + return ret; + +free_value: + json_object_put(value); + + return ret; +} + +static int json_set_int_internal(struct json_object *obj, const char *key, int64_t _value, + unsigned flags) +{ + struct json_object *value = json_object_new_int64(_value); + if (!value) { + json_errno("json_object_new_int"); + return -1; + } + + int ret = json_set_internal(obj, key, value, flags); + if (ret < 0) + goto free_value; + + return ret; + +free_value: + json_object_put(value); + + return ret; +} + +int json_set(struct json_object *obj, const char *key, struct json_object *value) +{ + return json_set_internal(obj, key, value, 0); +} + +int json_set_string(struct json_object *obj, const char *key, const char *value) +{ + return json_set_string_internal(obj, key, value, 0); +} + +int json_set_int(struct json_object *obj, const char *key, int64_t value) +{ + return json_set_int_internal(obj, key, value, 0); +} + +#ifndef JSON_C_OBJECT_ADD_CONSTANT_KEY +#define JSON_C_OBJECT_ADD_CONSTANT_KEY JSON_C_OBJECT_KEY_IS_CONSTANT +#endif +static const unsigned json_const_key_flags = JSON_C_OBJECT_ADD_CONSTANT_KEY; + +int json_set_const_key(struct json_object *obj, const char *key, struct json_object *value) +{ + return json_set_internal(obj, key, value, json_const_key_flags); +} + +int json_set_string_const_key(struct json_object *obj, const char *key, const char *value) +{ + return json_set_string_internal(obj, key, value, json_const_key_flags); +} + +int json_set_int_const_key(struct json_object *obj, const char *key, int64_t value) +{ + return json_set_int_internal(obj, key, value, json_const_key_flags); +} -- cgit v1.2.3