diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 3139254df..99637c2c9 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -172,12 +172,19 @@ mod core_functions { /// /// print(m); // prints #{"a":1, "b":2, "c":3} /// ``` - #[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_object"))] - #[cfg(feature = "metadata")] #[rhai_fn(return_raw)] pub fn parse_json(_ctx: NativeCallContext, json: &str) -> RhaiResultOf { - serde_json::from_str(json).map_err(|err| err.to_string().into()) + #[cfg(feature = "metadata")] + let out = serde_json::from_str(json).map_err(|err| err.to_string().into()); + + #[cfg(not(feature = "metadata"))] + let out = ctx + .engine() + .parse_json(json, true) + .map(|map_object| Dynamic::from(map_object)); + + out } } diff --git a/tests/eval.rs b/tests/eval.rs index 91e86ae97..de76d238f 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -92,7 +92,7 @@ fn test_eval_globals() { r#" const XYZ = 123; - fn foo() { global::XYZ } + fn foo() { global::XYZ } { eval("const XYZ = 42;"); } @@ -110,7 +110,7 @@ fn test_eval_globals() { r#" const XYZ = 123; - fn foo() { global::XYZ } + fn foo() { global::XYZ } eval("const XYZ = 42;"); diff --git a/tests/parse_json.rs b/tests/parse_json.rs new file mode 100644 index 000000000..7d1aeb97c --- /dev/null +++ b/tests/parse_json.rs @@ -0,0 +1,189 @@ +use rhai::{Engine, ParseErrorType, Scope, INT}; + +#[cfg(not(feature = "metadata"))] +mod without_metadata { + use super::*; + + #[test] + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_index"))] + #[cfg(not(feature = "no_object"))] + fn test_parse_json() { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let map = engine + .eval_with_scope::( + &mut scope, + r#" + parse_json("{\ + \"name\": \"John Doe\",\ + \"age\": 43,\ + \"address\": {\ + \"street\": \"10 Downing Street\",\ + \"city\": \"London\"\ + },\ + \"phones\": [\ + \"+44 1234567\",\ + \"+44 2345678\"\ + ]\ + }") + "#, + ) + .unwrap(); + + assert_eq!(map.len(), 4); + assert_eq!(map["name"].clone().into_immutable_string().expect("name should exist"), "John Doe"); + assert_eq!(map["age"].as_int().expect("age should exist"), 43); + assert_eq!(map["phones"].clone().into_typed_array::().expect("phones should exist"), ["+44 1234567", "+44 2345678"]); + + let address = map["address"].read_lock::().expect("address should exist"); + assert_eq!(address["city"].clone().into_immutable_string().expect("address.city should exist"), "London"); + assert_eq!(address["street"].clone().into_immutable_string().expect("address.street should exist"), "10 Downing Street"); + } + + #[test] + #[cfg(feature = "no_index")] + #[cfg(not(feature = "no_function"))] + fn test_parse_json_err_no_index() { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let err = engine + .eval_with_scope::( + &mut scope, + r#" + parse_json("{\ + \"v\": [\ + 1,\ + 2\ + ]\ + }") + "#, + ) + .unwrap_err(); + + assert!(matches!(err.as_ref(), rhai::EvalAltResult::ErrorParsing( + ParseErrorType::BadInput(LexError::UnexpectedInput(token)), pos) + if token == "[" && *pos == rhai::Position::new(1, 7))); + } + + #[test] + #[cfg(feature = "no_object")] + #[cfg(not(feature = "no_function"))] + fn test_parse_json_err_no_object() { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let err = engine + .eval_with_scope::( + &mut scope, + r#" + parse_json("{\ + \"v\": {\ + \"a\": 1,\ + \"b\": 2,\ + }\ + }") + "#, + ) + .unwrap_err(); + + assert!(matches!(err.as_ref(), rhai::EvalAltResult::ErrorFunctionNotFound(msg, pos) + if msg == "parse_json (&str | ImmutableString | String)" && *pos == rhai::Position::new(2, 13))); + } +} + +#[cfg(feature = "metadata")] +mod with_metadata { + use super::*; + + #[test] + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_index"))] + #[cfg(not(feature = "no_object"))] + fn test_parse_json() { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let map = engine + .eval_with_scope::( + &mut scope, + r#" + parse_json("{\ + \"name\": \"John Doe\",\ + \"age\": 43,\ + \"address\": {\ + \"street\": \"10 Downing Street\",\ + \"city\": \"London\"\ + },\ + \"phones\": [\ + \"+44 1234567\",\ + \"+44 2345678\"\ + ]\ + }") + "#, + ) + .unwrap(); + + assert_eq!(map.len(), 4); + assert_eq!(map["name"].clone().into_immutable_string().expect("name should exist"), "John Doe"); + assert_eq!(map["age"].as_int().expect("age should exist"), 43); + assert_eq!(map["phones"].clone().into_typed_array::().expect("phones should exist"), ["+44 1234567", "+44 2345678"]); + + let address = map["address"].read_lock::().expect("address should exist"); + assert_eq!(address["city"].clone().into_immutable_string().expect("address.city should exist"), "London"); + assert_eq!(address["street"].clone().into_immutable_string().expect("address.street should exist"), "10 Downing Street"); + } + + #[test] + #[cfg(feature = "no_index")] + #[cfg(not(feature = "no_function"))] + fn test_parse_json_err_no_index() { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let err = engine + .eval_with_scope::( + &mut scope, + r#" + parse_json("{\ + \"v\": [\ + 1,\ + 2\ + ]\ + }") + "#, + ) + .unwrap_err(); + + assert!(matches!(err.as_ref(), rhai::EvalAltResult::ErrorParsing( + ParseErrorType::BadInput(LexError::UnexpectedInput(token)), pos) + if token == "[" && *pos == rhai::Position::new(1, 7))); + } + + #[test] + #[cfg(feature = "no_object")] + #[cfg(not(feature = "no_function"))] + fn test_parse_json_err_no_object() { + let engine = Engine::new(); + let mut scope = Scope::new(); + + let err = engine + .eval_with_scope::( + &mut scope, + r#" + parse_json("{\ + \"v\": {\ + \"a\": 1,\ + \"b\": 2,\ + }\ + }") + "#, + ) + .unwrap_err(); + + assert!(matches!(err.as_ref(), rhai::EvalAltResult::ErrorFunctionNotFound(msg, pos) + if msg == "parse_json (&str | ImmutableString | String)" && *pos == rhai::Position::new(2, 13))); + } +}