mirror of
https://github.com/zlgopen/awtk.git
synced 2025-05-08 19:44:45 +08:00
conf_io support YAML
This commit is contained in:
parent
d580a30260
commit
3fd716f7d7
@ -1,5 +1,8 @@
|
||||
# 最新动态
|
||||
|
||||
2025/04/14
|
||||
* conf_io 支持 YAML格式。
|
||||
|
||||
2025/04/13
|
||||
* 完善slist/dlist(感谢兆坤提供补丁)
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# 读写 XML/JSON/INI 和 UBJSON 等格式的数据文件
|
||||
# 读写 XML/JSON/INI/YAML 和 UBJSON 等格式的数据文件
|
||||
|
||||
> 将抽象放进代码,细节放进元数据。
|
||||
|
||||
开发应用程序,会经常使用各种数据文件(如配置数据和元数据),常见的数据文件格式有 INI、XML、JSON 和 UBJSON,对一个复杂的应用程序,其中可能会同时使用多种不同格式的数据文件。
|
||||
开发应用程序,会经常使用各种数据文件(如配置数据和元数据),常见的数据文件格式有 INI、YAML、XML、JSON 和 UBJSON,对一个复杂的应用程序,其中可能会同时使用多种不同格式的数据文件。
|
||||
|
||||
通常,操作这些数据文件的函数各不相同,对于程序员来说即是学习负担,也是记忆负担。AWTK 提供了一套统一的接口函数,同一套接口函数,可以操作不同的格式的数据文件。
|
||||
|
||||
@ -315,6 +315,42 @@ tk_object_t* conf_ubjson_load(const char* url, bool_t create_if_not_exist);
|
||||
ret_t conf_ubjson_save_as(tk_object_t* obj, const char* url);
|
||||
```
|
||||
|
||||
### 3.5 YAML 格式
|
||||
|
||||
* 打开
|
||||
|
||||
```c
|
||||
/**
|
||||
* @method conf_yaml_load
|
||||
* 从指定 URL 加载 YAML 对象。
|
||||
*
|
||||
* @annotation ["constructor"]
|
||||
*
|
||||
* @param {const char*} url 路径(通常是文件路径)。
|
||||
* @param {bool_t} create_if_not_exist 如果不存在是否创建。
|
||||
*
|
||||
* @return {tk_object_t*} 返回配置对象。
|
||||
*/
|
||||
tk_object_t* conf_yaml_load(const char* url, bool_t create_if_not_exist);
|
||||
```
|
||||
|
||||
* 保存
|
||||
|
||||
```c
|
||||
/**
|
||||
* @method conf_yaml_save_as
|
||||
* 将 doc 对象保存到指定 URL。
|
||||
* @annotation ["static"]
|
||||
*
|
||||
* @param {tk_object_t*} obj doc 对象。
|
||||
* @param {const char*} url 保存的位置。
|
||||
*
|
||||
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败
|
||||
*/
|
||||
ret_t conf_yaml_save_as(tk_object_t* obj, const char* url);
|
||||
|
||||
```
|
||||
|
||||
## 完整示例
|
||||
|
||||
* 文件读写
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "tkc/data_reader_factory.h"
|
||||
#include "tkc/data_writer_factory.h"
|
||||
|
||||
#define INI_COMMENT_CHAR '#'
|
||||
|
||||
typedef enum _parser_state_t {
|
||||
STATE_NONE = 0,
|
||||
STATE_BEFORE_GROUP,
|
||||
@ -176,65 +178,6 @@ conf_doc_t* conf_doc_load_ini(const char* data) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_str(const char* p, str_t* str) {
|
||||
return_value_if_fail(p != NULL, RET_BAD_PARAMS);
|
||||
|
||||
while (*p) {
|
||||
if (*p == '#' || *p == '\\' || *p == '\n') {
|
||||
return_value_if_fail(str_append_char(str, '\\') == RET_OK, RET_OOM);
|
||||
}
|
||||
return_value_if_fail(str_append_char(str, *p) == RET_OK, RET_OOM);
|
||||
p++;
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_value(const value_t* v, str_t* str) {
|
||||
char buff[64] = {0};
|
||||
return_value_if_fail(str != NULL, RET_BAD_PARAMS);
|
||||
|
||||
switch (v->type) {
|
||||
case VALUE_TYPE_STRING: {
|
||||
const char* p = value_str(v);
|
||||
return_value_if_fail(p != NULL, RET_BAD_PARAMS);
|
||||
return conf_doc_save_str(p, str);
|
||||
}
|
||||
case VALUE_TYPE_WSTRING: {
|
||||
str_t s;
|
||||
ret_t ret = RET_OK;
|
||||
|
||||
str_init(&s, 0);
|
||||
str_from_wstr(&s, value_wstr(v));
|
||||
ret = conf_doc_save_str(s.str, str);
|
||||
str_reset(&s);
|
||||
return ret;
|
||||
}
|
||||
case VALUE_TYPE_FLOAT32: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%f", value_float32(v));
|
||||
break;
|
||||
}
|
||||
case VALUE_TYPE_FLOAT:
|
||||
case VALUE_TYPE_DOUBLE: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%lf", value_double(v));
|
||||
break;
|
||||
}
|
||||
case VALUE_TYPE_INT64: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%lld", value_int64(v));
|
||||
break;
|
||||
}
|
||||
case VALUE_TYPE_UINT64: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%llu", value_uint64(v));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%d", value_int(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return str_append(str, buff);
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_leaf_node(conf_node_t* node, str_t* str) {
|
||||
value_t v;
|
||||
const char* key = conf_node_get_name(node);
|
||||
@ -242,7 +185,7 @@ static ret_t conf_doc_save_leaf_node(conf_node_t* node, str_t* str) {
|
||||
conf_node_get_value(node, &v);
|
||||
|
||||
return_value_if_fail(str_append_more(str, " ", key, " = ", NULL) == RET_OK, RET_OOM);
|
||||
return_value_if_fail(conf_doc_save_value(&v, str) == RET_OK, RET_OOM);
|
||||
return_value_if_fail(conf_node_save_value(str, &v, INI_COMMENT_CHAR) == RET_OK, RET_OOM);
|
||||
return_value_if_fail(str_append(str, "\n") == RET_OK, RET_OOM);
|
||||
|
||||
return RET_OK;
|
||||
|
@ -1327,3 +1327,64 @@ ret_t conf_doc_foreach(conf_doc_t* doc, conf_doc_on_visit_t on_visit, void* ctx)
|
||||
return_value_if_fail(doc && on_visit, RET_BAD_PARAMS);
|
||||
return conf_node_foreach_sibling(NULL, conf_node_get_first_child(doc->root), on_visit, ctx);
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_str(const char* p, str_t* str, char comment_char) {
|
||||
return_value_if_fail(p != NULL, RET_BAD_PARAMS);
|
||||
|
||||
while (*p) {
|
||||
char c = str_escape_char(*p);
|
||||
if (c != *p || *p == comment_char || *p == '\\' || *p == '\n') {
|
||||
return_value_if_fail(str_append_char(str, '\\') == RET_OK, RET_OOM);
|
||||
}
|
||||
return_value_if_fail(str_append_char(str, c) == RET_OK, RET_OOM);
|
||||
p++;
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t conf_node_save_value(str_t* str, const value_t* v, char comment_char) {
|
||||
char buff[64] = {0};
|
||||
return_value_if_fail(str != NULL, RET_BAD_PARAMS);
|
||||
|
||||
switch (v->type) {
|
||||
case VALUE_TYPE_STRING: {
|
||||
const char* p = value_str(v);
|
||||
return_value_if_fail(p != NULL, RET_BAD_PARAMS);
|
||||
return conf_doc_save_str(p, str, comment_char);
|
||||
}
|
||||
case VALUE_TYPE_WSTRING: {
|
||||
str_t s;
|
||||
ret_t ret = RET_OK;
|
||||
|
||||
str_init(&s, 0);
|
||||
str_from_wstr(&s, value_wstr(v));
|
||||
ret = conf_doc_save_str(s.str, str, comment_char);
|
||||
str_reset(&s);
|
||||
return ret;
|
||||
}
|
||||
case VALUE_TYPE_FLOAT32: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%f", value_float32(v));
|
||||
break;
|
||||
}
|
||||
case VALUE_TYPE_FLOAT:
|
||||
case VALUE_TYPE_DOUBLE: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%lf", value_double(v));
|
||||
break;
|
||||
}
|
||||
case VALUE_TYPE_INT64: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%lld", value_int64(v));
|
||||
break;
|
||||
}
|
||||
case VALUE_TYPE_UINT64: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%llu", value_uint64(v));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
tk_snprintf(buff, sizeof(buff) - 1, "%d", value_int(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return str_append(str, buff);
|
||||
}
|
||||
|
@ -662,6 +662,9 @@ struct _conf_node_t {
|
||||
conf_node_t* first_child;
|
||||
binary_data_t binary_data;
|
||||
} value;
|
||||
|
||||
/*for yaml only*/
|
||||
uint8_t leading_spaces;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -967,6 +970,9 @@ ret_t conf_doc_foreach(conf_doc_t* doc, conf_doc_on_visit_t on_visit, void* ctx)
|
||||
ret_t conf_doc_set_ex(conf_doc_t* doc, conf_node_t* node, const char* path, const value_t* v);
|
||||
ret_t conf_doc_get_value_extend_type(conf_doc_t* doc, conf_node_t* node, value_t* v);
|
||||
|
||||
/*ini/yaml使用*/
|
||||
ret_t conf_node_save_value(str_t* str, const value_t* v, char comment_char);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_CONF_NODE_H*/
|
||||
|
410
src/conf_io/conf_yaml.c
Normal file
410
src/conf_io/conf_yaml.c
Normal file
@ -0,0 +1,410 @@
|
||||
/**
|
||||
* File: yaml.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: yaml
|
||||
*
|
||||
* Copyright (c) 2020 - 2025 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2025-04-13 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/mem.h"
|
||||
#include "tkc/utils.h"
|
||||
#include "conf_io/conf_yaml.h"
|
||||
#include "tkc/data_reader_mem.h"
|
||||
#include "tkc/data_writer_wbuffer.h"
|
||||
#include "tkc/data_reader_factory.h"
|
||||
#include "tkc/data_writer_factory.h"
|
||||
|
||||
/*root的leading_spaces为0,其它实际的节点均加上 YAML_LEADING_SPACE_OFFSET */
|
||||
#define YAML_LEADING_SPACE_OFFSET 2
|
||||
|
||||
#define YAML_SPACES_PER_LEVEL 2
|
||||
#define YAML_SEP_CHAR ':'
|
||||
#define YAML_COMMENT_CHAR '#'
|
||||
#define YAML_LIST_START_CHAR '-'
|
||||
|
||||
typedef struct _yaml_parser_t {
|
||||
str_t str;
|
||||
const char* data;
|
||||
const char* cursor;
|
||||
|
||||
conf_doc_t* doc;
|
||||
conf_node_t* current_node;
|
||||
} yaml_parser_t;
|
||||
|
||||
static ret_t yaml_parser_deinit(yaml_parser_t* parser);
|
||||
|
||||
static ret_t yaml_parser_init(yaml_parser_t* parser, const char* data) {
|
||||
conf_doc_t* doc = NULL;
|
||||
return_value_if_fail(parser != NULL && data != NULL, RET_BAD_PARAMS);
|
||||
|
||||
memset(parser, 0x00, sizeof(*parser));
|
||||
doc = conf_doc_create(100);
|
||||
return_value_if_fail(doc != NULL, RET_OOM);
|
||||
|
||||
doc->max_deep_level = 20;
|
||||
doc->root = conf_doc_create_node(doc, CONF_NODE_ROOT_NAME);
|
||||
if (doc->root == NULL) {
|
||||
TKMEM_FREE(doc);
|
||||
return RET_OOM;
|
||||
}
|
||||
|
||||
parser->doc = doc;
|
||||
goto_error_if_fail(str_init(&parser->str, 128) != NULL);
|
||||
|
||||
parser->data = data;
|
||||
parser->cursor = data;
|
||||
parser->current_node = doc->root;
|
||||
|
||||
return RET_OK;
|
||||
error:
|
||||
yaml_parser_deinit(parser);
|
||||
|
||||
return RET_FAIL;
|
||||
}
|
||||
|
||||
static ret_t yaml_parser_deinit(yaml_parser_t* parser) {
|
||||
return_value_if_fail(parser != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (parser->doc != NULL) {
|
||||
conf_doc_destroy(parser->doc);
|
||||
parser->doc = NULL;
|
||||
}
|
||||
|
||||
str_reset(&parser->str);
|
||||
memset(parser, 0x00, sizeof(*parser));
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static uint32_t yaml_parser_skip_leading_space(yaml_parser_t* parser) {
|
||||
uint32_t leading_spaces = 0;
|
||||
const char* p = parser->cursor;
|
||||
|
||||
while (*p) {
|
||||
if (*p == ' ') {
|
||||
leading_spaces++;
|
||||
} else if (*p == '\t') {
|
||||
leading_spaces += YAML_SPACES_PER_LEVEL;
|
||||
} else if (*p == '\r' || *p == '\n') {
|
||||
leading_spaces = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
parser->cursor = p;
|
||||
|
||||
return leading_spaces;
|
||||
}
|
||||
|
||||
static ret_t yaml_parser_skip_to_line_end(yaml_parser_t* parser) {
|
||||
parser->cursor = tk_skip_to_chars(parser->cursor, "\r\n");
|
||||
parser->cursor = tk_skip_chars(parser->cursor, "\r\n");
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static const char* yaml_parser_parse_value(yaml_parser_t* parser, uint32_t csep) {
|
||||
char c = '\0';
|
||||
uint32_t n = 0;
|
||||
str_t* s = &(parser->str);
|
||||
const char* p = parser->cursor;
|
||||
|
||||
if (*p != csep) {
|
||||
return NULL;
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
|
||||
str_clear(s);
|
||||
do {
|
||||
if (*p == '\\') {
|
||||
p++;
|
||||
if (*p == YAML_COMMENT_CHAR) {
|
||||
c = *p;
|
||||
p++;
|
||||
} else {
|
||||
c = str_unescape_char(p, &n);
|
||||
p += n;
|
||||
}
|
||||
} else if (*p == YAML_COMMENT_CHAR) {
|
||||
break;
|
||||
} else if (*p == '\r' || *p == '\n' || *p == '\0') {
|
||||
break;
|
||||
} else {
|
||||
c = *p++;
|
||||
}
|
||||
|
||||
str_append_char(s, c);
|
||||
} while(1);
|
||||
|
||||
parser->cursor = p;
|
||||
str_trim(s, " \t\r\n");
|
||||
yaml_parser_skip_to_line_end(parser);
|
||||
|
||||
return s->str;
|
||||
}
|
||||
|
||||
static const char* yaml_parser_parse_name(yaml_parser_t* parser) {
|
||||
const char* p = parser->cursor;
|
||||
str_t* s = &parser->str;
|
||||
|
||||
str_clear(s);
|
||||
do {
|
||||
char c = *p;
|
||||
if (c == YAML_SEP_CHAR) {
|
||||
break;
|
||||
} else if (c == '\0') {
|
||||
log_warn("yaml unexpected end\n");
|
||||
break;
|
||||
} else if (c == '\r' || c == '\n') {
|
||||
log_warn("yaml unexpected line end\n");
|
||||
break;
|
||||
} else if (tk_isalpha(c) || tk_isdigit(c) || c == '_') {
|
||||
str_append_char(s, *p);
|
||||
} else {
|
||||
log_warn("yaml invalid char '%c' for name\n", c);
|
||||
}
|
||||
p++;
|
||||
} while(1);
|
||||
|
||||
parser->cursor = p;
|
||||
str_trim(s, " \t\r\n");
|
||||
|
||||
return s->str;
|
||||
}
|
||||
|
||||
static conf_node_t* yaml_parser_get_parent_node(yaml_parser_t* parser, uint32_t leading_spaces) {
|
||||
conf_node_t* parent = parser->current_node;
|
||||
|
||||
while (parent->leading_spaces >= leading_spaces) {
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
static ret_t yaml_parser_parse_line(yaml_parser_t* parser) {
|
||||
ret_t ret = RET_OK;
|
||||
conf_node_t* node = NULL;
|
||||
conf_node_t* parent = NULL;
|
||||
const char* name = NULL;
|
||||
const char* value = NULL;
|
||||
char index_name[32] = {0};
|
||||
bool_t is_list = FALSE;
|
||||
uint32_t leading_spaces = yaml_parser_skip_leading_space(parser) + YAML_LEADING_SPACE_OFFSET;
|
||||
|
||||
parent = yaml_parser_get_parent_node(parser, leading_spaces);
|
||||
ENSURE(parent != NULL);
|
||||
|
||||
if (parser->cursor[0] == YAML_COMMENT_CHAR) {
|
||||
return yaml_parser_skip_to_line_end(parser);
|
||||
} else if (parser->cursor[0] == '\r' || parser->cursor[0] == '\n') {
|
||||
return yaml_parser_skip_to_line_end(parser);
|
||||
} else if (parser->cursor[0] == '\0') {
|
||||
return RET_EOS;
|
||||
} else if (parser->cursor[0] == YAML_LIST_START_CHAR) {
|
||||
uint32_t index = conf_node_count_children(parent);
|
||||
tk_snprintf(index_name, TK_NAME_LEN, "%u", index);
|
||||
name = index_name;
|
||||
is_list = TRUE;
|
||||
parent->node_type = CONF_NODE_ARRAY;
|
||||
} else {
|
||||
name = yaml_parser_parse_name(parser);
|
||||
}
|
||||
|
||||
node = conf_doc_create_node(parser->doc, name);
|
||||
return_value_if_fail(node != NULL, RET_OOM);
|
||||
node->leading_spaces = leading_spaces;
|
||||
|
||||
value = yaml_parser_parse_value(parser, is_list ? YAML_LIST_START_CHAR : YAML_SEP_CHAR);
|
||||
if (value != NULL && *value) {
|
||||
value_t v;
|
||||
value_set_str(&v, value);
|
||||
conf_node_set_value(node, &v);
|
||||
}
|
||||
|
||||
ret = conf_doc_append_child(parser->doc, parent, node);
|
||||
if (ret == RET_OK) {
|
||||
parser->current_node = node;
|
||||
} else {
|
||||
conf_doc_destroy_node(parser->doc, node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t yaml_parser_parse(yaml_parser_t* parser) {
|
||||
do {
|
||||
ret_t ret = yaml_parser_parse_line(parser);
|
||||
if (ret == RET_EOS) {
|
||||
break;
|
||||
} else if (ret != RET_OK) {
|
||||
return ret;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
conf_doc_t* conf_doc_load_yaml(const char* data) {
|
||||
ret_t ret = RET_OK;
|
||||
yaml_parser_t parser;
|
||||
conf_doc_t* doc = NULL;
|
||||
return_value_if_fail(yaml_parser_init(&parser, data) == RET_OK, NULL);
|
||||
|
||||
ret = yaml_parser_parse(&parser);
|
||||
if (ret == RET_OK) {
|
||||
doc = parser.doc;
|
||||
parser.doc = NULL;
|
||||
}
|
||||
|
||||
yaml_parser_deinit(&parser);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_yaml_node_name_value(conf_node_t* node, str_t* str, uint32_t levels) {
|
||||
value_t v;
|
||||
ret_t ret = RET_OK;
|
||||
const char* key = conf_node_get_name(node);
|
||||
|
||||
if (levels > 0) {
|
||||
ret = str_append_n_chars(str, ' ', levels * 2);
|
||||
return_value_if_fail(ret == RET_OK, ret);
|
||||
}
|
||||
|
||||
if (node->parent->node_type == CONF_NODE_ARRAY) {
|
||||
return_value_if_fail(str_append_char(str, YAML_LIST_START_CHAR) == RET_OK, RET_OOM);
|
||||
} else {
|
||||
return_value_if_fail(str_append(str, key) == RET_OK, RET_OOM);
|
||||
return_value_if_fail(str_append_char(str, YAML_SEP_CHAR) == RET_OK, RET_OOM);
|
||||
}
|
||||
|
||||
ret = conf_node_get_value(node, &v);
|
||||
if (ret == RET_OK) {
|
||||
return_value_if_fail(str_append(str, " ") == RET_OK, RET_OOM);
|
||||
return_value_if_fail(conf_node_save_value(str, &v, YAML_COMMENT_CHAR) == RET_OK, RET_OOM);
|
||||
}
|
||||
return_value_if_fail(str_append(str, "\n") == RET_OK, RET_OOM);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_yaml_node(conf_node_t* node, str_t* str, uint32_t levels) {
|
||||
return_value_if_fail(node != NULL && str != NULL, RET_BAD_PARAMS);
|
||||
|
||||
while (node != NULL) {
|
||||
conf_doc_save_yaml_node_name_value(node, str, levels);
|
||||
|
||||
if (node->value_type == CONF_NODE_VALUE_NODE) {
|
||||
conf_node_t* iter = conf_node_get_first_child(node);
|
||||
conf_doc_save_yaml_node(iter, str, levels + 1);
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t conf_doc_save_yaml(conf_doc_t* doc, str_t* str) {
|
||||
conf_node_t* node = NULL;
|
||||
return_value_if_fail(doc != NULL && str != NULL, RET_BAD_PARAMS);
|
||||
str_clear(str);
|
||||
|
||||
node = conf_node_get_first_child(doc->root);
|
||||
return_value_if_fail(node != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return conf_doc_save_yaml_node(node, str, 0);
|
||||
}
|
||||
|
||||
static conf_doc_t* conf_doc_load_yaml_reader(data_reader_t* reader) {
|
||||
char* data = NULL;
|
||||
int32_t rsize = 0;
|
||||
conf_doc_t* doc = NULL;
|
||||
uint64_t size = data_reader_get_size(reader);
|
||||
return_value_if_fail(reader != NULL && size > 0, NULL);
|
||||
data = TKMEM_ALLOC(size + 1);
|
||||
return_value_if_fail(data != NULL, NULL);
|
||||
|
||||
memset(data, 0x00, size + 1);
|
||||
rsize = data_reader_read(reader, 0, data, size);
|
||||
if (rsize > 0) {
|
||||
doc = conf_doc_load_yaml(data);
|
||||
}
|
||||
TKMEM_FREE(data);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
static ret_t conf_doc_save_yaml_writer(conf_doc_t* doc, data_writer_t* writer) {
|
||||
str_t str;
|
||||
return_value_if_fail(writer != NULL, RET_BAD_PARAMS);
|
||||
|
||||
goto_error_if_fail(str_init(&str, 1024) != NULL);
|
||||
goto_error_if_fail(conf_doc_save_yaml(doc, &str) == RET_OK);
|
||||
goto_error_if_fail(data_writer_write(writer, 0, str.str, str.size) == str.size);
|
||||
str_reset(&str);
|
||||
|
||||
return RET_OK;
|
||||
|
||||
error:
|
||||
str_reset(&str);
|
||||
|
||||
return RET_FAIL;
|
||||
}
|
||||
|
||||
tk_object_t* conf_yaml_load(const char* url, bool_t create_if_not_exist) {
|
||||
return conf_obj_create(conf_doc_save_yaml_writer, conf_doc_load_yaml_reader, url,
|
||||
create_if_not_exist);
|
||||
}
|
||||
|
||||
ret_t conf_yaml_save_as(tk_object_t* obj, const char* url) {
|
||||
data_writer_t* writer = NULL;
|
||||
conf_doc_t* doc = conf_obj_get_doc(obj);
|
||||
return_value_if_fail(doc != NULL && url != NULL, RET_BAD_PARAMS);
|
||||
writer = data_writer_factory_create_writer(data_writer_factory(), url);
|
||||
return_value_if_fail(writer != NULL, RET_BAD_PARAMS);
|
||||
|
||||
conf_doc_save_yaml_writer(doc, writer);
|
||||
data_writer_destroy(writer);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
tk_object_t* conf_yaml_create(void) {
|
||||
return conf_yaml_load(NULL, TRUE);
|
||||
}
|
||||
|
||||
tk_object_t* conf_yaml_load_from_buff(const void* buff, uint32_t size, bool_t create_if_not_exist) {
|
||||
char url[MAX_PATH + 1] = {0};
|
||||
return_value_if_fail(buff != NULL, NULL);
|
||||
data_reader_mem_build_url(buff, size, url);
|
||||
|
||||
return conf_yaml_load(url, create_if_not_exist);
|
||||
}
|
||||
|
||||
ret_t conf_yaml_save_to_buff(tk_object_t* obj, wbuffer_t* wb) {
|
||||
char url[MAX_PATH + 1] = {0};
|
||||
return_value_if_fail(obj != NULL && wb != NULL, RET_BAD_PARAMS);
|
||||
|
||||
wbuffer_init_extendable(wb);
|
||||
data_writer_wbuffer_build_url(wb, url);
|
||||
|
||||
return conf_yaml_save_as(obj, url);
|
||||
}
|
171
src/conf_io/conf_yaml.h
Normal file
171
src/conf_io/conf_yaml.h
Normal file
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* File: yaml.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: yaml
|
||||
*
|
||||
* Copyright (c) 2020 - 2025 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2025-04-13 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_CONF_YAML_H
|
||||
#define TK_CONF_YAML_H
|
||||
|
||||
#include "tkc/str.h"
|
||||
#include "tkc/buffer.h"
|
||||
#include "conf_io/conf_obj.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class conf_yaml_t
|
||||
* @parent tk_object_t
|
||||
* @annotation ["fake"]
|
||||
*
|
||||
* conf yaml对象。
|
||||
*
|
||||
* 示例
|
||||
*
|
||||
*```c
|
||||
* char filename[MAX_PATH + 1] = {0};
|
||||
* path_prepend_temp_path(filename, "test.yaml");
|
||||
*
|
||||
* const char *yaml_data1 = "root:\n"
|
||||
* " name:awplc\n"
|
||||
* " age:18\n"
|
||||
* " weight:60.5\n";
|
||||
* ENSURE(file_write(filename, yaml_data1, strlen(yaml_data1)) == RET_OK);
|
||||
*
|
||||
* // 从文件加载
|
||||
* tk_object_t *yaml = conf_yaml_load(filename, FALSE);
|
||||
*
|
||||
* // 获取数据。
|
||||
* ENSURE(tk_str_eq(tk_object_get_prop_str(yaml, "root.name"), "awplc"));
|
||||
* ENSURE(tk_object_get_prop_int(yaml, "root.age", 0) == 18);
|
||||
* ENSURE(tk_object_get_prop_double(yaml, "root.weight", 0) == 60.5);
|
||||
*
|
||||
* // 销毁对象
|
||||
* TK_OBJECT_UNREF(yaml);
|
||||
*
|
||||
* // 从内存加载
|
||||
* yaml = conf_yaml_load_from_buff(yaml_data1, strlen(yaml_data1), FALSE);
|
||||
*
|
||||
* // 获取数据
|
||||
* ENSURE(tk_str_eq(tk_object_get_prop_str(yaml, "root.name"), "awplc"));
|
||||
* ENSURE(tk_object_get_prop_int(yaml, "root.age", 0) == 18);
|
||||
* ENSURE(tk_object_get_prop_double(yaml, "root.weight", 0) == 60.5);
|
||||
*
|
||||
* // 设置数据
|
||||
* ENSURE(tk_object_set_prop_int(yaml, "root.age", 20) == RET_OK);
|
||||
* ENSURE(tk_object_get_prop_int(yaml, "root.age", 0) == 20);
|
||||
*
|
||||
* // 保存到文件
|
||||
* ENSURE(conf_yaml_save_as(yaml, filename) == RET_OK);
|
||||
* ENSURE(file_exist(filename) == TRUE);
|
||||
*
|
||||
* // 销毁对象
|
||||
* TK_OBJECT_UNREF(yaml);
|
||||
*```
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method conf_yaml_create
|
||||
* 创建一个空的conf对象。
|
||||
* @annotation ["constructor"]
|
||||
*
|
||||
* @return {tk_object_t*} 返回配置对象。
|
||||
*/
|
||||
tk_object_t* conf_yaml_create(void);
|
||||
|
||||
/**
|
||||
* @method conf_yaml_load
|
||||
* 从指定URL加载YAML对象。
|
||||
*
|
||||
* @annotation ["constructor"]
|
||||
*
|
||||
* @param {const char*} url 路径(通常是文件路径)。
|
||||
* @param {bool_t} create_if_not_exist 如果不存在是否创建。
|
||||
*
|
||||
* @return {tk_object_t*} 返回配置对象。
|
||||
*/
|
||||
tk_object_t* conf_yaml_load(const char* url, bool_t create_if_not_exist);
|
||||
|
||||
/**
|
||||
* @method conf_yaml_load_from_buff
|
||||
* 从内存加载YAML对象。
|
||||
* @annotation ["constructor"]
|
||||
*
|
||||
* @param {const void*} buff 数据。
|
||||
* @param {uint32_t} size 数据长度。
|
||||
* @param {bool_t} create_if_not_exist 如果不存在是否创建。
|
||||
*
|
||||
* @return {tk_object_t*} 返回配置对象。
|
||||
*/
|
||||
tk_object_t* conf_yaml_load_from_buff(const void* buff, uint32_t size, bool_t create_if_not_exist);
|
||||
|
||||
/**
|
||||
* @method conf_yaml_save_to_buff
|
||||
* 将obj保存为YAML格式到内存。
|
||||
*
|
||||
* @param {tk_object_t*} obj doc对象。
|
||||
* @param {wbuffer_t*} wb 返回结果(不要初始化,使用完成后要调用wbuffer_deyamlt)。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败
|
||||
*/
|
||||
ret_t conf_yaml_save_to_buff(tk_object_t* obj, wbuffer_t* wb);
|
||||
|
||||
/**
|
||||
* @method conf_yaml_save_as
|
||||
* 将doc对象保存到指定URL。
|
||||
* @annotation ["static"]
|
||||
*
|
||||
* @param {tk_object_t*} obj doc对象。
|
||||
* @param {const char*} url 保存的位置。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败
|
||||
*/
|
||||
ret_t conf_yaml_save_as(tk_object_t* obj, const char* url);
|
||||
|
||||
/*public for test*/
|
||||
|
||||
/**
|
||||
* @method conf_doc_load_yaml
|
||||
*
|
||||
* 从缓存区加载yaml格式的conf doc对象。
|
||||
*
|
||||
* @annotation ["global"]
|
||||
*
|
||||
* @param {const char*} data 数据。
|
||||
*
|
||||
* @return {conf_doc_t*} 返回conf_doc对象。
|
||||
*/
|
||||
conf_doc_t* conf_doc_load_yaml(const char* data);
|
||||
|
||||
/**
|
||||
* @method conf_doc_save_yaml
|
||||
*
|
||||
* 保存conf doc对象为yaml格式。
|
||||
* @annotation ["global"]
|
||||
*
|
||||
* @param {conf_doc_t*} doc conf doc对象。
|
||||
* @param {str_t*} str 保存结果。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败
|
||||
*/
|
||||
ret_t conf_doc_save_yaml(conf_doc_t* doc, str_t* str);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_CONF_YAML_H*/
|
||||
|
@ -370,7 +370,7 @@ ret_t str_encode_xml_entity_with_len(str_t* str, const char* text, uint32_t len)
|
||||
}
|
||||
|
||||
/*https://en.wikipedia.org/wiki/Escape_sequences_in_C*/
|
||||
static char str_escape_char(char c) {
|
||||
char str_escape_char(char c) {
|
||||
switch (c) {
|
||||
case '\a': {
|
||||
c = 'a';
|
||||
@ -412,7 +412,7 @@ static char str_escape_char(char c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
static char str_unescape_char(const char* s, uint32_t* nr) {
|
||||
char str_unescape_char(const char* s, uint32_t* nr) {
|
||||
char c = 0;
|
||||
const char* start = s;
|
||||
return_value_if_fail(s != NULL && nr != NULL, 0);
|
||||
|
@ -853,6 +853,25 @@ ret_t str_append_format_padding(str_t* str, uint32_t size, const char* format, .
|
||||
*/
|
||||
ret_t str_append_json_pair(str_t* str, const char* key, const value_t* value);
|
||||
|
||||
/**
|
||||
* @method str_escape_char
|
||||
* 转义字符。
|
||||
* > 如:'\n'转义为"n"。
|
||||
* @param {char} c 字符。
|
||||
* @return {char} 返回转义后的字符。
|
||||
*/
|
||||
char str_escape_char(char c);
|
||||
|
||||
/**
|
||||
* @method str_unescape_char
|
||||
* 反转义字符。
|
||||
* > 如:"n"反转义为'\n'。
|
||||
* @param {const char*} s 字符串。
|
||||
* @param {uint32_t*} nr 返回读取的字符数。
|
||||
* @return {char} 返回反转义后的字符。
|
||||
*/
|
||||
char str_unescape_char(const char* s, uint32_t* nr);
|
||||
|
||||
#define STR_DESTROY(str) \
|
||||
if (str != NULL) { \
|
||||
str_destroy(str); \
|
||||
|
464
tests/conf_yaml_test.cc
Normal file
464
tests/conf_yaml_test.cc
Normal file
@ -0,0 +1,464 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "conf_io/conf_yaml.h"
|
||||
|
||||
TEST(Yaml, basic1) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("hello:");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "hello");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "hello");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "hello:\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, basic2) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("name: jim");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "name");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "name");
|
||||
|
||||
ASSERT_STREQ(conf_node_get_child_value_str(doc->root, "name", ""), "jim");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: jim\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, basic3) {
|
||||
value_t v;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("person:\r\n name: jim\n age:100");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_STREQ(conf_doc_get_str(doc, "person.name", ""), "jim");
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "person.age", 0), 100);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "person:\n name: jim\n age: 100\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, basic4) {
|
||||
value_t v;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("jim:\r\n age:100\ntom:\n age:99\n");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 2);
|
||||
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "jim.age", 0), 100);
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "tom.age", 0), 99);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "jim:\n age: 100\ntom:\n age: 99\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, basic5) {
|
||||
value_t v;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("jim:\r\n age:100\n weight: 60");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "jim.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 2);
|
||||
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "jim.age", 0), 100);
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "jim.weight", 0), 60);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "jim:\n age: 100\n weight: 60\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, basic6) {
|
||||
value_t v;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("jim:\r\n age:100\n weight: 60\ntom:\n age:99\n weight:70");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 2);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "jim.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 2);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "tom.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 2);
|
||||
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "jim.age", 0), 100);
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "tom.age", 0), 99);
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "jim.weight", 0), 60);
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "tom.weight", 0), 70);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "jim:\n age: 100\n weight: 60\ntom:\n age: 99\n weight: 70\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, basic7) {
|
||||
value_t v;
|
||||
const char* data = "plan_request_params:\n\
|
||||
planning_attempts: 1\n\
|
||||
planning_pipeline: ompl\n\
|
||||
max_velocity_scaling_factor: 1.0\n\
|
||||
max_acceleration_scaling_factor: 0.5 ";
|
||||
|
||||
conf_doc_t* doc = conf_doc_load_yaml(data);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "plan_request_params.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 4);
|
||||
|
||||
ASSERT_EQ(conf_doc_get_int(doc, "plan_request_params.planning_attempts", 0), 1);
|
||||
ASSERT_STREQ(conf_doc_get_str(doc, "plan_request_params.planning_pipeline", ""), "ompl");
|
||||
ASSERT_FLOAT_EQ(conf_doc_get_float(doc, "plan_request_params.max_velocity_scaling_factor", 0), 1.0f);
|
||||
ASSERT_FLOAT_EQ(conf_doc_get_float(doc, "plan_request_params.max_acceleration_scaling_factor", 0), 0.5f);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "plan_request_params:\n planning_attempts: 1\n planning_pipeline: ompl\n max_velocity_scaling_factor: 1.0\n max_acceleration_scaling_factor: 0.5\n");
|
||||
str_reset(&str);
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, list1) {
|
||||
value_t v;
|
||||
const char* data = "planning_pipelines:\n\
|
||||
pipeline_names:\n\
|
||||
- ompl\n";
|
||||
|
||||
conf_doc_t* doc = conf_doc_load_yaml(data);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "planning_pipelines.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "planning_pipelines.pipeline_names.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "planning_pipelines:\n pipeline_names:\n - ompl\n");
|
||||
str_reset(&str);
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, list2) {
|
||||
value_t v;
|
||||
const char* data = "planning_pipelines:\n\
|
||||
pipeline_names:\n\
|
||||
- ompl\n\
|
||||
- kdl\n\
|
||||
- ikfast\n";
|
||||
|
||||
conf_doc_t* doc = conf_doc_load_yaml(data);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "planning_pipelines.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "planning_pipelines.pipeline_names.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 3);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "planning_pipelines:\n pipeline_names:\n - ompl\n - kdl\n - ikfast\n");
|
||||
str_reset(&str);
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, comment1) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("name: jim #comment");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "name");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "name");
|
||||
|
||||
ASSERT_STREQ(conf_node_get_child_value_str(doc->root, "name", ""), "jim");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: jim\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, comment2) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("#comment\nname: jim #comment");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "name");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "name");
|
||||
|
||||
ASSERT_STREQ(conf_node_get_child_value_str(doc->root, "name", ""), "jim");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: jim\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, comment3) {
|
||||
value_t v;
|
||||
const char* data = "planning_pipelines:#comment\n\
|
||||
pipeline_names:#comment\n\
|
||||
- ompl#comment\n\
|
||||
#comment\n\
|
||||
- kdl#comment\n\
|
||||
- ikfast #comment\n";
|
||||
|
||||
conf_doc_t* doc = conf_doc_load_yaml(data);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "planning_pipelines.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "planning_pipelines.pipeline_names.#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 3);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "planning_pipelines:\n pipeline_names:\n - ompl\n - kdl\n - ikfast\n");
|
||||
str_reset(&str);
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, error1) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("hello");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "hello");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "hello");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "hello:\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, error2) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("hel\tlo");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "hello");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "hello");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "hello:\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, error3) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("hel#lo");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "hello");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "hello");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "hello:\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, escape1) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("name: hello\\nworld");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "name");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "name");
|
||||
|
||||
const char* value = conf_node_get_child_value_str(doc->root, "name", "");
|
||||
ASSERT_STREQ(value, "hello\nworld");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: hello\\nworld\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, escape2) {
|
||||
value_t v;
|
||||
conf_node_t* node = NULL;
|
||||
conf_doc_t* doc = conf_doc_load_yaml("name: \\#hello");
|
||||
|
||||
ASSERT_EQ(conf_doc_get(doc, "#size", &v), RET_OK);
|
||||
ASSERT_EQ(value_int(&v), 1);
|
||||
|
||||
node = conf_node_find_child(doc->root, "name");
|
||||
ASSERT_EQ(node != NULL, true);
|
||||
ASSERT_STREQ(conf_node_get_name(node), "name");
|
||||
|
||||
const char* value = conf_node_get_child_value_str(doc->root, "name", "");
|
||||
ASSERT_STREQ(value, "#hello");
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: \\#hello\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, set1) {
|
||||
conf_doc_t* doc = conf_doc_load_yaml("name: \\#hello");
|
||||
|
||||
conf_doc_set_str(doc, "name", "tom");
|
||||
conf_doc_set_int(doc, "age", 100);
|
||||
conf_doc_set_float(doc, "weight", 60);
|
||||
conf_doc_set_bool(doc, "male", true);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: tom\nage: 100\nweight: 60.000000\nmale: 1\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, set2) {
|
||||
conf_doc_t* doc = conf_doc_load_yaml("name: \\#hello");
|
||||
|
||||
conf_doc_set_int(doc, "tom.age", 100);
|
||||
conf_doc_set_float(doc, "tom.weight", 60);
|
||||
conf_doc_set_bool(doc, "tom.male", true);
|
||||
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
conf_doc_save_yaml(doc, &str);
|
||||
ASSERT_STREQ(str.str, "name: \\#hello\ntom:\n age: 100\n weight: 60.000000\n male: 1\n");
|
||||
str_reset(&str);
|
||||
|
||||
conf_doc_destroy(doc);
|
||||
}
|
||||
|
||||
TEST(Yaml, set3) {
|
||||
wbuffer_t wb;
|
||||
tk_object_t* conf = conf_yaml_create();
|
||||
ASSERT_NE(conf, (tk_object_t*)NULL);
|
||||
ASSERT_EQ(tk_object_set_prop_int(conf, "awtk.value", 123), RET_OK);
|
||||
ASSERT_EQ(tk_object_get_prop_int(conf, "awtk.value", 0), 123);
|
||||
|
||||
ASSERT_EQ(conf_yaml_save_to_buff(conf, &wb), RET_OK);
|
||||
TK_OBJECT_UNREF(conf);
|
||||
|
||||
conf = conf_yaml_load_from_buff(wb.data, wb.cursor, FALSE);
|
||||
ASSERT_EQ(tk_object_get_prop_int(conf, "awtk.value", 0), 123);
|
||||
TK_OBJECT_UNREF(conf);
|
||||
|
||||
wbuffer_deinit(&wb);
|
||||
}
|
||||
|
||||
TEST(Yaml, file) {
|
||||
tk_object_t* conf = conf_yaml_load("file://./tests/testdata/test.yaml", TRUE);
|
||||
|
||||
ASSERT_STREQ(tk_object_get_prop_str(conf, "plan_request_params.planning_pipeline"), "ompl");
|
||||
ASSERT_STREQ(tk_object_get_prop_str(conf, "plan_request_params.planning_attempts"), "1");
|
||||
ASSERT_STREQ(tk_object_get_prop_str(conf, "plan_request_params.max_velocity_scaling_factor"), "1.0");
|
||||
ASSERT_STREQ(tk_object_get_prop_str(conf, "plan_request_params.max_acceleration_scaling_factor"), "1.0");
|
||||
|
||||
ASSERT_STREQ(tk_object_get_prop_str(conf, "planning_pipelines.pipeline_names.[0]"), "ompl");
|
||||
ASSERT_STREQ(tk_object_get_prop_str(conf, "planning_pipelines.pipeline_names.[1]"), "kdl");
|
||||
|
||||
|
||||
TK_OBJECT_UNREF(conf);
|
||||
}
|
@ -1137,3 +1137,47 @@ TEST(Str, append_format_ex) {
|
||||
|
||||
str_reset(&s);
|
||||
}
|
||||
|
||||
TEST(Str, unescape_char1) {
|
||||
str_t s;
|
||||
uint32_t n = 0;
|
||||
str_init(&s, 0);
|
||||
const char* data = "abc\\r\\nd";
|
||||
const char* p = data;
|
||||
|
||||
while (*p != '\0') {
|
||||
if (*p == '\\') {
|
||||
p++;
|
||||
char c = str_unescape_char(p, &n);
|
||||
str_append_char(&s, c);
|
||||
p += n;
|
||||
} else {
|
||||
str_append_char(&s, *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
ASSERT_STREQ(s.str, "abc\r\nd");
|
||||
|
||||
str_reset(&s);
|
||||
}
|
||||
|
||||
TEST(Str, escape_char1) {
|
||||
str_t s;
|
||||
str_init(&s, 0);
|
||||
const char* data = "abc\r\nd";
|
||||
const char* p = data;
|
||||
|
||||
while (*p != '\0') {
|
||||
char c = str_escape_char(*p);
|
||||
if (*p != c) {
|
||||
str_append_char(&s, '\\');
|
||||
str_append_char(&s, c);
|
||||
} else {
|
||||
str_append_char(&s, *p);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
ASSERT_STREQ(s.str, "abc\\r\\nd");
|
||||
|
||||
str_reset(&s);
|
||||
}
|
19
tests/testdata/test.yaml
vendored
Normal file
19
tests/testdata/test.yaml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
planning_scene_monitor_options:
|
||||
name: "planning_scene_monitor"
|
||||
robot_description: "robot_description"
|
||||
joint_state_topic: "/joint_states"
|
||||
attached_collision_object_topic: "/planning_scene_monitor"
|
||||
publish_planning_scene_topic: "/publish_planning_scene"
|
||||
monitored_planning_scene_topic: "/monitored_planning_scene"
|
||||
wait_for_initial_state_timeout: 10.0
|
||||
|
||||
planning_pipelines:
|
||||
pipeline_names:
|
||||
- ompl
|
||||
- kdl
|
||||
|
||||
plan_request_params:
|
||||
planning_attempts: 1
|
||||
planning_pipeline: ompl
|
||||
max_velocity_scaling_factor: 1.0
|
||||
max_acceleration_scaling_factor: 1.0
|
Loading…
x
Reference in New Issue
Block a user