diff --git a/include/to_json.h b/include/to_json.h index c93c353..a728d81 100644 --- a/include/to_json.h +++ b/include/to_json.h @@ -7,6 +7,6 @@ #include #include -void free_rust_string(const char *string); +void free_rust_string(const char *ptr); -const char *to_json_rust(const char *content); +const char *to_json_ffi(const char *content); diff --git a/src/common/json.c b/src/common/json.c index 1b303dd..4264824 100644 --- a/src/common/json.c +++ b/src/common/json.c @@ -17,8 +17,8 @@ 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 (if failed -> return NULL) - const char *json_string = to_json_rust(content); // convert to json format +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 @@ -30,6 +30,7 @@ char* to_json(const char *content) { // convert JSON / TOML / YAML to json forma 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/to-json/Cargo.lock b/src/to-json/Cargo.lock index 71b6233..7b043cc 100644 --- a/src/to-json/Cargo.lock +++ b/src/to-json/Cargo.lock @@ -16,9 +16,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -38,15 +38,15 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", diff --git a/src/to-json/Cargo.toml b/src/to-json/Cargo.toml index 70e5be9..d4202a8 100644 --- a/src/to-json/Cargo.toml +++ b/src/to-json/Cargo.toml @@ -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.87" +serde_json = "1.0.89" serde_yaml = "0.9.14" toml = "0.5.9" diff --git a/src/to-json/src/convert.rs b/src/to-json/src/convert.rs new file mode 100644 index 0000000..9393fad --- /dev/null +++ b/src/to-json/src/convert.rs @@ -0,0 +1,20 @@ +use crate::parser::{parser, Value}; + +fn json_convert(content: &str) -> Result { // convert to JSON format + let data = match parser(content)? { + Value::JSON(json) => serde_json::to_string(&json), + Value::YAML(yaml) => serde_json::to_string(&yaml), + Value::TOML(toml) => serde_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, + } +} diff --git a/src/to-json/src/ffi.rs b/src/to-json/src/ffi.rs index c9569be..dcaf66d 100644 --- a/src/to-json/src/ffi.rs +++ b/src/to-json/src/ffi.rs @@ -1,14 +1,27 @@ +use crate::convert::to_json; use std::ffi::{c_char, CStr, CString}; -use crate::json::to_json; + +fn to_c_string(string: String) -> *const c_char { // fetch c-style ptr of string + CString::new(string).unwrap().into_raw() +} + +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() + ) +} #[no_mangle] -pub unsafe extern "C" fn free_rust_string(string: *const c_char) { - let _ = CString::from_raw(string as *mut _); +pub unsafe extern "C" fn free_rust_string(ptr: *const c_char) { // free string memory + let _ = CString::from_raw(ptr as *mut _); } #[no_mangle] -pub unsafe extern "C" fn to_json_rust(content: *const c_char) -> *const c_char { - let content: &str = CStr::from_ptr(content).to_str().unwrap(); - let content: String = to_json(content); // may return empty string - CString::new(content).unwrap().into_raw() +pub unsafe extern "C" fn to_json_ffi(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 } diff --git a/src/to-json/src/json.rs b/src/to-json/src/json.rs deleted file mode 100644 index 83efc4c..0000000 --- a/src/to-json/src/json.rs +++ /dev/null @@ -1,32 +0,0 @@ -use serde_json as json; -use serde_yaml as yaml; - -enum Format { - JSON(json::Value), - YAML(yaml::Value), - TOML(toml::Value), -} - -fn parser(content: &str) -> Option { - if let Ok(data) = json::from_str::(content) { // try JSON format - return Some(Format::JSON(data)); - } - if let Ok(data) = toml::from_str::(content) { // try TOML format - return Some(Format::TOML(data)); - } - if let Ok(data) = yaml::from_str::(content) { // try YAML format - return Some(Format::YAML(data)); - } - return None; // parse failed -} - -pub fn to_json(content: &str) -> String { // convert to JSON format - match parser(content) { - Some(data) => match data { - Format::JSON(dat) => json::to_string(&dat).unwrap(), - Format::YAML(dat) => json::to_string(&dat).unwrap(), - Format::TOML(dat) => json::to_string(&dat).unwrap(), - }, - None => String::from(""), // failed -> empty string - } -} diff --git a/src/to-json/src/lib.rs b/src/to-json/src/lib.rs index 4f1b9a0..f04b9c3 100644 --- a/src/to-json/src/lib.rs +++ b/src/to-json/src/lib.rs @@ -1,2 +1,4 @@ mod ffi; -mod json; +mod tests; +mod parser; +mod convert; diff --git a/src/to-json/src/parser.rs b/src/to-json/src/parser.rs new file mode 100644 index 0000000..1d84d18 --- /dev/null +++ b/src/to-json/src/parser.rs @@ -0,0 +1,43 @@ +use serde_json as json; +use serde_yaml as yaml; + +#[derive(Debug)] +pub enum Value { + JSON(json::Value), + YAML(yaml::Value), + TOML(toml::Value), +} + +fn json_parser(content: &str) -> Option { // parse json content + match json::from_str::(content) { + Ok(result) => Some(result), + Err(_) => None, + } +} + +fn yaml_parser(content: &str) -> Option { // parse yaml content + match yaml::from_str::(content) { + Ok(result) => Some(result), + Err(_) => None, + } +} + +fn toml_parser(content: &str) -> Option { // parse toml content + match toml::from_str::(content) { + Ok(result) => Some(result), + Err(_) => None, + } +} + +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")), + } + } + } +} diff --git a/src/to-json/src/tests.rs b/src/to-json/src/tests.rs new file mode 100644 index 0000000..ee74871 --- /dev/null +++ b/src/to-json/src/tests.rs @@ -0,0 +1,80 @@ +use crate::convert::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()), + ); + } +}