Browse Source

refactor: to_json rust module

dev
Dnomd343 2 years ago
parent
commit
9665b6af77
  1. 3
      include/common/json.h
  2. 11
      include/to_json.h
  3. 4
      src/CMakeLists.txt
  4. 2
      src/applet/adguard.c
  5. 8
      src/assets/Cargo.lock
  6. 5
      src/assets/src/fetch.rs
  7. 3
      src/assets/src/ffi.rs
  8. 14
      src/common/json.c
  9. 2
      src/loader/default.c
  10. 2
      src/loader/parser.c
  11. 79
      src/to-json/Cargo.lock
  12. 8
      src/to-json/Cargo.toml
  13. 2
      src/to-json/cbindgen.toml
  14. 135
      src/to-json/src/ffi.rs
  15. 21
      src/to-json/src/json.rs
  16. 2
      src/to-json/src/lib.rs
  17. 62
      src/to-json/src/parser.rs
  18. 80
      src/to-json/src/tests.rs

3
include/common/json.h

@ -4,7 +4,7 @@
#include <stdint.h>
#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

11
include/to_json.h

@ -1,12 +1,17 @@
#pragma once
/* Generated with cbindgen:0.23.0 */
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/**
* 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);

4
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)

2
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);

8
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"

5
src/assets/src/fetch.rs

@ -92,3 +92,8 @@ pub(crate) async fn asset_fetch(name: &str, sources: &Vec<String>) -> Option<Vec
info!("Asset `{}` fetch complete with {} items", name, contents.len());
Some(contents)
}
#[cfg(test)]
mod tests {
// TODO: add test items
}

3
src/assets/src/ffi.rs

@ -70,6 +70,7 @@ pub async unsafe extern "C" fn asset_update(
debug!("Asset sources -> {:?}", 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);
}

14
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

2
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);
}

2
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");
}

79
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",
]

8
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"

2
src/to-json/cbindgen.toml

@ -1,3 +1,3 @@
language = "C"
pragma_once = true
include_version = true
include_version = false

135
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<String> {
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()),
);
}
}

21
src/to-json/src/json.rs

@ -1,21 +0,0 @@
use serde_json as json;
use crate::parser::{parser, Value};
fn json_convert(content: &str) -> Result<String, String> { // 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<String> { // to JSON string
match json_convert(content) {
Ok(data) => Some(data),
Err(_) => None, // convert failed
}
}

2
src/to-json/src/lib.rs

@ -1,4 +1,2 @@
mod ffi;
mod json;
mod tests;
mod parser;

62
src/to-json/src/parser.rs

@ -8,36 +8,88 @@ pub enum Value {
TOML(toml::Value),
}
fn json_parser(content: &str) -> Option<json::Value> { // parse json content
/// Deserialize text content into JSON format.
fn json_parser(content: &str) -> Option<json::Value> {
match json::from_str::<json::Value>(content) {
Ok(result) => Some(result),
Err(_) => None,
}
}
fn yaml_parser(content: &str) -> Option<yaml::Value> { // parse yaml content
/// Deserialize text content into YAML format.
fn yaml_parser(content: &str) -> Option<yaml::Value> {
match yaml::from_str::<yaml::Value>(content) {
Ok(result) => Some(result),
Err(_) => None,
}
}
fn toml_parser(content: &str) -> Option<toml::Value> { // parse toml content
/// Deserialize text content into TOML format.
fn toml_parser(content: &str) -> Option<toml::Value> {
match toml::from_str::<toml::Value>(content) {
Ok(result) => Some(result),
Err(_) => None,
}
}
pub fn parser(content: &str) -> Result<Value, String> {
/// Try to deserialize the text in JSON, TOML and YAML format.
pub fn parser(content: &str) -> Result<Value, &'static str> {
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
}
}

80
src/to-json/src/tests.rs

@ -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()),
);
}
}
Loading…
Cancel
Save