use crate::error::{Error, Result};
use crate::value::{ObjectMap, Value};
use super::array_item::render_array_item;
use super::object::render_object_body;
pub fn render(value: &Value) -> Result<String> {
let mut out = String::with_capacity(estimate_size(value));
match value {
Value::Object(o) => render_object_body(o, 0, &mut out)?,
Value::Array(items) => {
for item in items {
render_array_item(item, 0, &mut out)?;
}
}
_ => {
return Err(Error::Message(
"top-level value must be an Object or an Array".into(),
))
}
}
Ok(out)
}
pub fn to_string_force_strings(value: &Value) -> Result<String> {
let coerced = coerce_scalars_to_strings(value);
render(&coerced)
}
fn coerce_scalars_to_strings(value: &Value) -> Value {
match value {
Value::Null => Value::String("null".into()),
Value::Bool(true) => Value::String("true".into()),
Value::Bool(false) => Value::String("false".into()),
Value::Integer(s) => Value::String(s.clone()),
Value::Float(s) => Value::String(s.clone()),
Value::String(s) => Value::String(s.clone()),
Value::Array(items) => Value::Array(items.iter().map(coerce_scalars_to_strings).collect()),
Value::Object(obj) => {
let mut out = ObjectMap::with_capacity_and_hasher(obj.len(), Default::default());
for (k, v) in obj {
out.insert(k.clone(), coerce_scalars_to_strings(v));
}
Value::Object(out)
}
}
}
fn estimate_size(value: &Value) -> usize {
match value {
Value::Null => 5, Value::Bool(_) => 6, Value::Integer(s) | Value::Float(s) | Value::String(s) => s.len() + 4,
Value::Array(items) => 4 + items.iter().map(estimate_size).sum::<usize>(),
Value::Object(obj) => obj
.iter()
.map(|(k, v)| k.len() + 4 + estimate_size(v))
.sum::<usize>()
.saturating_add(4),
}
}