From 9665b6af77e13be444c3e5be18f1882ea38a3430 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Mon, 6 Mar 2023 19:49:44 +0800 Subject: [PATCH] refactor: to_json rust module --- include/common/json.h | 3 +- include/to_json.h | 11 +++- src/CMakeLists.txt | 4 +- src/applet/adguard.c | 2 +- src/assets/Cargo.lock | 8 +-- src/assets/src/fetch.rs | 5 ++ src/assets/src/ffi.rs | 3 +- src/common/json.c | 14 ++-- src/loader/default.c | 2 +- src/loader/parser.c | 2 +- src/to-json/Cargo.lock | 79 +++++++++++++++++----- src/to-json/Cargo.toml | 8 +-- src/to-json/cbindgen.toml | 2 +- src/to-json/src/ffi.rs | 135 +++++++++++++++++++++++++++++++++----- src/to-json/src/json.rs | 21 ------ src/to-json/src/lib.rs | 2 - src/to-json/src/parser.rs | 62 +++++++++++++++-- src/to-json/src/tests.rs | 80 ---------------------- 18 files changed, 275 insertions(+), 168 deletions(-) delete mode 100644 src/to-json/src/json.rs delete mode 100644 src/to-json/src/tests.rs diff --git a/include/common/json.h b/include/common/json.h index 345f408..c3a4cfc 100644 --- a/include/common/json.h +++ b/include/common/json.h @@ -4,7 +4,7 @@ #include #include "cJSON.h" -char* to_json(const char *content); +char* to_json_format(const char *content); uint8_t is_json_suffix(const char *file_name); cJSON* json_field_get(cJSON *entry, const char *key); void json_field_replace(cJSON *entry, const char *key, cJSON *content); @@ -14,6 +14,5 @@ uint8_t json_bool_value(char *caption, cJSON *json); char* json_string_value(char* caption, cJSON *json); char** json_string_list_value(char *caption, cJSON *json, char **string_list); uint32_t** json_uint32_list_value(char *caption, cJSON *json, uint32_t **uint32_list); -void json_string_map_value(char *caption, cJSON *json, char ***key_list, char ***value_list); #endif diff --git a/include/to_json.h b/include/to_json.h index a728d81..b1b7f46 100644 --- a/include/to_json.h +++ b/include/to_json.h @@ -1,12 +1,17 @@ #pragma once -/* Generated with cbindgen:0.23.0 */ - #include #include #include #include +/** + * Free the exported c-style string. + */ void free_rust_string(const char *ptr); -const char *to_json_ffi(const char *content); +/** + * Format the input text into JSON format and return a c-style string, or return + * `NULL` if an error occurs. + */ +const char *to_json(const char *content); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6a05c9..3a06801 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,11 +7,9 @@ include_directories(${PROJECT_SOURCE_DIR}/include/bcrypt) include_directories(${PROJECT_SOURCE_DIR}/include/common) include_directories(${PROJECT_SOURCE_DIR}/include/loader) +link_directories(${PROJECT_SOURCE_DIR}/src/assets/target/release) link_directories(${PROJECT_SOURCE_DIR}/src/to-json/target/release) -# TODO: just for test -link_directories(${PROJECT_SOURCE_DIR}/src/assets/target/debug) - add_subdirectory(utils) add_subdirectory(applet) add_subdirectory(bcrypt) diff --git a/src/applet/adguard.c b/src/applet/adguard.c index 74c0d23..1a8e2ab 100644 --- a/src/applet/adguard.c +++ b/src/applet/adguard.c @@ -110,7 +110,7 @@ process* adguard_load(adguard *info, const char *dir) { // load adguard options adguard_config_ret = adguard_config(info, "{}"); // begin with empty json } else { // configure exist -> modify char *adguard_config_content = read_file(adguard_config_file); - char *adguard_config_json = to_json(adguard_config_content); + char *adguard_config_json = to_json_format(adguard_config_content); adguard_config_ret = adguard_config(info, adguard_config_json); free(adguard_config_content); free(adguard_config_json); diff --git a/src/assets/Cargo.lock b/src/assets/Cargo.lock index 5e23ef0..ac298b5 100644 --- a/src/assets/Cargo.lock +++ b/src/assets/Cargo.lock @@ -770,9 +770,9 @@ checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -951,9 +951,9 @@ checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" diff --git a/src/assets/src/fetch.rs b/src/assets/src/fetch.rs index b5a6e5e..15b135d 100644 --- a/src/assets/src/fetch.rs +++ b/src/assets/src/fetch.rs @@ -92,3 +92,8 @@ pub(crate) async fn asset_fetch(name: &str, sources: &Vec) -> Option {:?}", sources); debug!("Asset target -> `{}`", file); + // TODO: clear target file at first match asset_fetch(&name, &sources).await { Some(data) => { let mut content = String::new(); @@ -82,7 +83,7 @@ pub async unsafe extern "C" fn asset_update( .create(true) .open(&file) { // open target file Ok(mut fp) => { - match fp.write_all(content.trim().as_ref()) { + match fp.write_all(content.as_ref()) { Err(err) => { warn!("File `{}` save error: {}", file, err); } diff --git a/src/common/json.c b/src/common/json.c index ac008ab..eda5809 100644 --- a/src/common/json.c +++ b/src/common/json.c @@ -17,20 +17,18 @@ uint8_t is_json_suffix(const char *file_name) { // whether file name end with `. return FALSE; } -char* to_json(const char *content) { // convert JSON / TOML / YAML to json format (failed -> NULL) - const char *json_string = to_json_ffi(content); // convert to json format - char *json_content = strdup(json_string); // load string into owner heap - free_rust_string(json_string); // free rust string - if (strlen(json_content) == 0) { // empty string -> convert error +char* to_json_format(const char *content) { // convert JSON / TOML / YAML to json format (failed -> NULL) + const char *json_string = to_json(content); // convert to json format + if (json_string == NULL) { log_warn("JSON convert error ->\n%s", content); - free(json_content); - return NULL; + return NULL; // convert failed } + char *json_content = strdup(json_string); // load string into owner heap + free_rust_string(json_string); // free rust string log_debug("JSON convert result ->\n%s", json_content); return json_content; } - cJSON* json_field_get(cJSON *entry, const char *key) { // fetch key from json map (create when key not exist) cJSON *sub = entry->child; while (sub != NULL) { // traverse all keys diff --git a/src/loader/default.c b/src/loader/default.c index 44a13b8..ade85f7 100644 --- a/src/loader/default.c +++ b/src/loader/default.c @@ -57,7 +57,7 @@ void load_default_config(const char *config_file) { log_info("Loading default configure file"); char *config_content = NULL; if (is_json_suffix(config_file)) { // convert to json format - config_content = to_json(DEFAULT_CONFIG); + config_content = to_json_format(DEFAULT_CONFIG); } else { config_content = strdup(DEFAULT_CONFIG); } diff --git a/src/loader/parser.c b/src/loader/parser.c index 8bb68c2..b6f2c66 100644 --- a/src/loader/parser.c +++ b/src/loader/parser.c @@ -199,7 +199,7 @@ void config_parser(cleardns_config *config, const char *config_file) { log_info("Start JSON configure parser"); } else { // YAML or TOML format log_info("Start configure parser"); - char *convert_ret = to_json(config_content); + char *convert_ret = to_json_format(config_content); if (convert_ret == NULL) { // convert failed log_fatal("Configure parser error"); } diff --git a/src/to-json/Cargo.lock b/src/to-json/Cargo.lock index 7b043cc..d4ecb0f 100644 --- a/src/to-json/Cargo.lock +++ b/src/to-json/Cargo.lock @@ -26,38 +26,53 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" -version = "1.0.149" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "serde_yaml" -version = "0.9.14" +version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da" +checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" dependencies = [ "indexmap", "itoa", @@ -67,7 +82,7 @@ dependencies = [ ] [[package]] -name = "to-json" +name = "to_json" version = "0.1.0" dependencies = [ "serde_json", @@ -77,15 +92,49 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] name = "unsafe-libyaml" -version = "0.2.4" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" + +[[package]] +name = "winnow" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" +checksum = "c95fb4ff192527911dd18eb138ac30908e7165b8944e528b6af93aa4c842d345" +dependencies = [ + "memchr", +] diff --git a/src/to-json/Cargo.toml b/src/to-json/Cargo.toml index d4202a8..c2a0eda 100644 --- a/src/to-json/Cargo.toml +++ b/src/to-json/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "to-json" +name = "to_json" version = "0.1.0" edition = "2021" @@ -9,6 +9,6 @@ crate-type = ["staticlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde_json = "1.0.89" -serde_yaml = "0.9.14" -toml = "0.5.9" +serde_json = "1.0.94" +serde_yaml = "0.9.19" +toml = "0.7.2" diff --git a/src/to-json/cbindgen.toml b/src/to-json/cbindgen.toml index cb8d3fd..0555597 100644 --- a/src/to-json/cbindgen.toml +++ b/src/to-json/cbindgen.toml @@ -1,3 +1,3 @@ language = "C" pragma_once = true -include_version = true +include_version = false diff --git a/src/to-json/src/ffi.rs b/src/to-json/src/ffi.rs index f728598..5f147c8 100644 --- a/src/to-json/src/ffi.rs +++ b/src/to-json/src/ffi.rs @@ -1,27 +1,130 @@ -use crate::json::to_json; -use std::ffi::{c_char, CStr, CString}; +use std::os::raw::c_char; +use std::ffi::{CStr, CString}; +use crate::parser::{parser, Value}; -fn to_c_string(string: String) -> *const c_char { // fetch c-style ptr of string - CString::new(string).unwrap().into_raw() +/// Load c-style string from `const char *` pointer. +#[inline] +unsafe fn load_c_string(ptr: *const c_char) -> String { + CString::from(CStr::from_ptr(ptr)) + .into_string() + .unwrap() } -unsafe fn load_c_string(ptr: *const c_char) -> String { // load string from c-style ptr - String::from( - CStr::from_ptr(ptr).to_str().unwrap() - ) +/// Export c-style string as `const char *` pointer. +/// # NOTE +/// The exported string cannot be freed by the c language `free(void *)` function, +/// but should use the `free_rust_string` callback function, if this interface is +/// not called, a memory leak will occur. +#[inline] +fn export_c_string(string: String) -> *const c_char { + CString::new(string).unwrap().into_raw() } +/// Free the exported c-style string. #[no_mangle] -pub unsafe extern "C" fn free_rust_string(ptr: *const c_char) { // free string memory - let _ = CString::from_raw(ptr as *mut _); +pub unsafe extern "C" fn free_rust_string(ptr: *const c_char) { + let _ = CString::from_raw(ptr as *mut _); // reclaim rust ownership +} + +/// Deserialize text content and serialize to JSON format. +fn json_format(content: &str) -> Option { + let result = match parser(&content) { + Ok(value) => match value { + Value::JSON(json) => serde_json::to_string(&json), + Value::YAML(yaml) => serde_json::to_string(&yaml), + Value::TOML(toml) => serde_json::to_string(&toml), + }, + _ => return None, + }; + match result { + Ok(data) => Some(data), + Err(_) => None, + } } +/// Format the input text into JSON format and return a c-style string, or return +/// `NULL` if an error occurs. #[no_mangle] -pub unsafe extern "C" fn to_json_ffi(content: *const c_char) -> *const c_char { +pub unsafe extern "C" fn to_json(content: *const c_char) -> *const c_char { let content = load_c_string(content); - let result = match to_json(&content) { // convert to JSON format - Some(data) => data, - None => String::new(), // convert failed -> empty string - }; - to_c_string(result) // return c-style ptr + match json_format(&content) { + Some(data) => export_c_string(data), + None => std::ptr::null(), + } +} + +#[cfg(test)] +mod tests { + use super::json_format; + + const JSON_TEST_STR: &str = r#" +{ + "int": 123, + "bool": true, + "float": 3.141592, + "string": "json test", + "array": [1, 2, 3, 4, 5], + "object": { + "sub": "test" + } +} +"#; + + const YAML_TEST_STR: &str = r#" +int: 123 +bool: true +float: 3.141592 +string: "json test" +array: + - 1 + - 2 + - 3 + - 4 + - 5 +object: + sub: test +"#; + + const TOML_TEST_STR: &str = r#" +int = 123 +bool = true +float = 3.141592 +string = "json test" +array = [ 1, 2, 3, 4, 5 ] + +[object] +sub = "test" +"#; + + #[inline] + fn format(raw: &str) -> String { + match json_format(raw) { + Some(data) => data, + None => panic!("format error"), + } + } + + #[test] + fn json_input() { + assert_eq!( + format(JSON_TEST_STR), + format(&json_format(JSON_TEST_STR).unwrap()), + ); + } + + #[test] + fn yaml_input() { + assert_eq!( + format(JSON_TEST_STR), + format(&json_format(YAML_TEST_STR).unwrap()), + ); + } + + #[test] + fn toml_input() { + assert_eq!( + format(JSON_TEST_STR), + format(&json_format(TOML_TEST_STR).unwrap()), + ); + } } diff --git a/src/to-json/src/json.rs b/src/to-json/src/json.rs deleted file mode 100644 index 8f79d44..0000000 --- a/src/to-json/src/json.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde_json as json; -use crate::parser::{parser, Value}; - -fn json_convert(content: &str) -> Result { // convert to JSON format - let data = match parser(content)? { - Value::JSON(_json) => json::to_string(&_json), - Value::YAML(_yaml) => json::to_string(&_yaml), - Value::TOML(_toml) => json::to_string(&_toml), - }; - match data { - Ok(data) => Ok(data), - Err(err) => Err(err.to_string()), - } -} - -pub fn to_json(content: &str) -> Option { // to JSON string - match json_convert(content) { - Ok(data) => Some(data), - Err(_) => None, // convert failed - } -} diff --git a/src/to-json/src/lib.rs b/src/to-json/src/lib.rs index d5af57d..b8ea04a 100644 --- a/src/to-json/src/lib.rs +++ b/src/to-json/src/lib.rs @@ -1,4 +1,2 @@ mod ffi; -mod json; -mod tests; mod parser; diff --git a/src/to-json/src/parser.rs b/src/to-json/src/parser.rs index 1d84d18..1adc7c3 100644 --- a/src/to-json/src/parser.rs +++ b/src/to-json/src/parser.rs @@ -8,36 +8,88 @@ pub enum Value { TOML(toml::Value), } -fn json_parser(content: &str) -> Option { // parse json content +/// Deserialize text content into JSON format. +fn json_parser(content: &str) -> Option { match json::from_str::(content) { Ok(result) => Some(result), Err(_) => None, } } -fn yaml_parser(content: &str) -> Option { // parse yaml content +/// Deserialize text content into YAML format. +fn yaml_parser(content: &str) -> Option { match yaml::from_str::(content) { Ok(result) => Some(result), Err(_) => None, } } -fn toml_parser(content: &str) -> Option { // parse toml content +/// Deserialize text content into TOML format. +fn toml_parser(content: &str) -> Option { match toml::from_str::(content) { Ok(result) => Some(result), Err(_) => None, } } -pub fn parser(content: &str) -> Result { +/// Try to deserialize the text in JSON, TOML and YAML format. +pub fn parser(content: &str) -> Result { match json_parser(content) { // try JSON format Some(data) => Ok(Value::JSON(data)), None => match toml_parser(content) { // try TOML format Some(data) => Ok(Value::TOML(data)), None => match yaml_parser(content) { // try YAML format Some(data) => Ok(Value::YAML(data)), - None => Err(String::from("unknown input format")), + None => Err("unknown input format"), } } } } + +#[cfg(test)] +mod tests { + use super::Value; + use super::parser; + use super::json_parser; + use super::yaml_parser; + use super::toml_parser; + + const JSON_STR: &str = "{\"test\": \"ok\"}"; + const YAML_STR: &str = "test: ok"; + const TOML_STR: &str = "test = \"ok\""; + + #[test] + fn json() { + assert!(json_parser("").is_none()); // parse invalid text + assert!(json_parser(JSON_STR).is_some()); + } + + #[test] + fn yaml() { + assert!(yaml_parser("").is_none()); // parse invalid text + assert!(yaml_parser(YAML_STR).is_some()); + } + + #[test] + fn toml() { + assert!(toml_parser(".").is_none()); // parse invalid text + assert!(toml_parser(TOML_STR).is_some()); + } + + #[test] + fn global() { + match parser(JSON_STR).unwrap() { + Value::JSON(_) => (), + _ => panic!("JSON parser error"), + }; + match parser(YAML_STR).unwrap() { + Value::YAML(_) => (), + _ => panic!("YAML parser error"), + }; + match parser(TOML_STR).unwrap() { + Value::TOML(_) => (), + _ => panic!("TOML parser error"), + }; + assert!(parser("\0").is_err()); // parse invalid text + } +} diff --git a/src/to-json/src/tests.rs b/src/to-json/src/tests.rs deleted file mode 100644 index 3c832c9..0000000 --- a/src/to-json/src/tests.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::json::to_json; - -#[allow(dead_code)] -const JSON_TEST_CONTENT: &str = r#" -{ - "int": 123, - "bool": true, - "float": 3.141592, - "string": "json test", - "array": [1, 2, 3, 4, 5], - "object": { - "sub": "test" - } -} -"#; - -#[allow(dead_code)] -const YAML_TEST_CONTENT: &str = r#" -int: 123 -bool: true -float: 3.141592 -string: "json test" -array: - - 1 - - 2 - - 3 - - 4 - - 5 -object: - sub: test -"#; - -#[allow(dead_code)] -const TOML_TEST_CONTENT: &str = r#" -int = 123 -bool = true -float = 3.141592 -string = "json test" -array = [ 1, 2, 3, 4, 5 ] - -[object] -sub = "test" -"#; - - -mod tests { - use super::*; - - #[allow(dead_code)] - fn format_json(raw: &str) -> String { - match to_json(raw) { - Some(data) => data, - None => panic!("JSON format error"), - } - } - - #[test] - fn json_to_json() { - assert_eq!( - format_json(JSON_TEST_CONTENT), - format_json(&to_json(JSON_TEST_CONTENT).unwrap()), - ); - } - - #[test] - fn yaml_to_json() { - assert_eq!( - format_json(JSON_TEST_CONTENT), - format_json(&to_json(YAML_TEST_CONTENT).unwrap()), - ); - } - - #[test] - fn toml_to_json() { - assert_eq!( - format_json(JSON_TEST_CONTENT), - format_json(&to_json(TOML_TEST_CONTENT).unwrap()), - ); - } -}