Skip to content

Commit

Permalink
Fix format json bug.
Browse files Browse the repository at this point in the history
  • Loading branch information
schungx committed Dec 13, 2023
1 parent d5b427f commit 1bca3a7
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 9 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ Fixes to bugs found via fuzzing
* Indexing into an array with a negative index that is larger than the length of the array now throws an out-of-bounds error (similar to positive indices) instead of defaulting to the first element.
* Fixed edge-case crash in timestamp functions.

Other bug fixes
---------------

* Arrays in object maps now serialize to JSON correctly via `to_json()` when the `serde` feature is not enabled.
* `Engine::format_map_as_json` now serializes arrays correctly.

Enhancements
------------

Expand Down
55 changes: 47 additions & 8 deletions src/api/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
use crate::func::native::locked_write;
use crate::parser::{ParseSettingFlags, ParseState};
use crate::tokenizer::{lex_raw, Token};
use crate::types::dynamic::Union;
use crate::types::StringsInterner;
use crate::{Engine, LexError, Map, RhaiResultOf};
use crate::{Dynamic, Engine, LexError, Map, RhaiResultOf};
use std::fmt::Write;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

Expand Down Expand Up @@ -166,23 +168,60 @@ pub fn format_map_as_json(map: &Map) -> String {
let mut result = String::from('{');

for (key, value) in map {
use std::fmt::Write;

if result.len() > 1 {
result.push(',');
}

write!(result, "{key:?}").unwrap();
result.push(':');

match value.read_lock::<Map>() {
Some(val) => result.push_str(&format_map_as_json(&val)),
None if value.is_unit() => result.push_str("null"),
None => write!(result, "{value:?}").unwrap(),
}
format_dynamic_as_json(&mut result, value);
}

result.push('}');

result
}

/// Format a [`Dynamic`] value as JSON.
fn format_dynamic_as_json(result: &mut String, value: &Dynamic) {
match value.0 {
Union::Unit(..) => result.push_str("null"),
Union::FnPtr(ref f, _, _) if f.is_curried() => {
result.push('[');
write!(result, "{:?}", f.fn_name()).unwrap();
f.iter_curry().for_each(|value| {
result.push(',');
format_dynamic_as_json(result, value);
});
result.push(']');
}
Union::FnPtr(ref f, _, _) => write!(result, "{:?}", f.fn_name()).unwrap(),
Union::Map(ref m, ..) => result.push_str(&format_map_as_json(&m)),
#[cfg(not(feature = "no_index"))]
Union::Array(ref a, _, _) => {
result.push('[');
for (i, x) in a.iter().enumerate() {
if i > 0 {
result.push(',');
}
format_dynamic_as_json(result, x);
}
result.push(']');
}
#[cfg(not(feature = "no_index"))]
Union::Blob(ref b, _, _) => {
result.push('[');
for (i, x) in b.iter().enumerate() {
if i > 0 {
result.push(',');
}
write!(result, "{x}").unwrap();
}
result.push(']');
}
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref v, _, _) => format_dynamic_as_json(result, &*v.borrow()),

Check failure on line 224 in src/api/json.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,serde,metadata,internals,debugging, stable,...

no method named `borrow` found for reference `&Arc<RwLock<dynamic::Dynamic>>` in the current scope
_ => write!(result, "{value:?}").unwrap(),
}
}
2 changes: 1 addition & 1 deletion src/ast/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl fmt::Debug for OpAssignment {
.field("pos", &self.pos)
.finish()
} else {
fmt::Debug::fmt(&self.pos, f)
write!(f, "{} @ {:?}", Token::Equals, self.pos)
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/serde/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::types::dynamic::Union;
use crate::{Dynamic, ImmutableString, Scope};
use serde::{ser::SerializeSeq, Serialize, Serializer};
use std::iter::once;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

Expand Down Expand Up @@ -63,6 +64,9 @@ impl Serialize for Dynamic {
m.iter().try_for_each(|(k, v)| map.serialize_entry(k, v))?;
map.end()
}
Union::FnPtr(ref f, ..) if f.is_curried() => {
ser.collect_seq(once(f.fn_name().into()).chain(f.iter_curry().cloned()))
}
Union::FnPtr(ref f, ..) => ser.serialize_str(f.fn_name()),
#[cfg(not(feature = "no_time"))]
Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()),
Expand Down
4 changes: 4 additions & 0 deletions tests/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ fn test_map_json() {
.len(),
11
);

assert_eq!(engine.eval::<String>("#{a:[#{b:42}]}.to_json()").unwrap(), r#"{"a":[{"b":42}]}"#);
assert_eq!(engine.eval::<String>(r#"#{a:[Fn("abc")]}.to_json()"#).unwrap(), r#"{"a":["abc"]}"#);
assert_eq!(engine.eval::<String>(r#"#{a:[Fn("abc").curry(42).curry(123)]}.to_json()"#).unwrap(), r#"{"a":[["abc",42,123]]}"#);
}

engine.parse_json(json, true).unwrap();
Expand Down

0 comments on commit 1bca3a7

Please sign in to comment.