use crate::{Error, Number, Result, ToonMap, ToonOptions, Value};
use serde::ser::SerializeSeq;
use serde::{ser, Serialize};
pub struct Serializer {
output: String,
options: ToonOptions,
indent_level: usize,
}
impl Serializer {
pub fn new(options: ToonOptions) -> Self {
Serializer {
output: String::with_capacity(256),
options,
indent_level: 0,
}
}
pub fn into_inner(self) -> String {
self.output
}
fn write_newline(&mut self) {
if self.options.pretty {
self.output.push('\n');
}
}
#[inline]
fn needs_quotes(s: &str) -> bool {
s.is_empty()
|| s.contains(':')
|| s.contains(',')
|| s.contains('\n')
|| s.contains('\t')
|| s.contains('|')
|| s.contains('"')
|| s.contains('\\')
|| s.contains('\0')
|| s.starts_with(' ')
|| s.ends_with(' ')
|| s == "true"
|| s == "false"
|| s == "null"
|| s.parse::<f64>().is_ok()
}
#[inline]
fn write_string(&mut self, s: &str) {
if Self::needs_quotes(s) {
self.output.push('"');
for ch in s.chars() {
match ch {
'"' => self.output.push_str("\\\""),
'\\' => self.output.push_str("\\\\"),
'\n' => self.output.push_str("\\n"),
'\r' => self.output.push_str("\\r"),
'\t' => self.output.push_str("\\t"),
'\u{0008}' => self.output.push_str("\\b"), '\u{000C}' => self.output.push_str("\\f"), '\0' => self.output.push_str("\\0"),
_ => self.output.push(ch),
}
}
self.output.push('"');
} else {
self.output.push_str(s);
}
}
}
impl<'a> ser::Serializer for &'a mut Serializer {
type Ok = ();
type Error = Error;
type SerializeSeq = SeqSerializer<'a>;
type SerializeTuple = TupleSerializer<'a>;
type SerializeTupleStruct = TupleStructSerializer<'a>;
type SerializeTupleVariant = TupleVariantSerializer<'a>;
type SerializeMap = MapSerializer<'a>;
type SerializeStruct = StructSerializer<'a>;
type SerializeStructVariant = StructVariantSerializer<'a>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
self.output.push_str(if v { "true" } else { "false" });
Ok(())
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
self.serialize_i64(v as i64)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
self.serialize_i64(v as i64)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
self.serialize_i64(v as i64)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
self.output.push_str(&v.to_string());
Ok(())
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
self.serialize_u64(v as u64)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
self.serialize_u64(v as u64)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
self.serialize_u64(v as u64)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
self.output.push_str(&v.to_string());
Ok(())
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
self.serialize_f64(v as f64)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
self.output.push_str(&v.to_string());
Ok(())
}
fn serialize_char(self, v: char) -> Result<Self::Ok> {
self.serialize_str(&v.to_string())
}
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
self.write_string(v);
Ok(())
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
use ser::SerializeSeq;
let mut seq = self.serialize_seq(Some(v.len()))?;
for byte in v {
seq.serialize_element(byte)?;
}
seq.end()
}
fn serialize_none(self) -> Result<Self::Ok> {
self.serialize_unit()
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok> {
self.output.push_str("null");
Ok(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok> {
self.serialize_str(variant)
}
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok>
where
T: ?Sized + Serialize,
{
self.output.push_str(variant);
self.output.push(':');
if self.options.pretty {
self.output.push(' ');
}
value.serialize(self)
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
Ok(SeqSerializer {
ser: self,
elements: Vec::new(),
})
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
Ok(TupleSerializer {
ser: self,
elements: Vec::new(),
})
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct> {
Ok(TupleStructSerializer {
ser: self,
elements: Vec::new(),
})
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant> {
Ok(TupleVariantSerializer {
ser: self,
variant: variant.to_string(),
elements: Vec::new(),
})
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
Ok(MapSerializer {
ser: self,
entries: Vec::new(),
current_key: None,
})
}
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
Ok(StructSerializer {
ser: self,
entries: Vec::new(),
})
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant> {
Ok(StructVariantSerializer {
ser: self,
variant: variant.to_string(),
entries: Vec::new(),
})
}
}
pub struct SeqSerializer<'a> {
ser: &'a mut Serializer,
elements: Vec<Value>,
}
impl<'a> ser::SerializeSeq for SeqSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let toon_value = to_toon_value(value)?;
self.elements.push(toon_value);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
if self.elements.is_empty() {
self.ser.output.push_str("[0]:");
return Ok(());
}
let tabular = can_be_tabular(&self.elements);
if let Some((headers, rows)) = tabular {
write_tabular_array(
&mut self.ser.output,
&headers,
&rows,
&self.ser.options,
self.ser.indent_level,
);
} else {
let all_primitives = self.elements.iter().all(is_primitive_value);
if all_primitives {
write_inline_array(&mut self.ser.output, &self.elements, &self.ser.options);
} else {
write_list_array(
&mut self.ser.output,
&self.elements,
&self.ser.options,
self.ser.indent_level,
);
}
}
Ok(())
}
}
pub struct TupleSerializer<'a> {
ser: &'a mut Serializer,
elements: Vec<Value>,
}
impl<'a> ser::SerializeTuple for TupleSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let toon_value = to_toon_value(value)?;
self.elements.push(toon_value);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
let seq_ser = SeqSerializer {
ser: self.ser,
elements: self.elements,
};
seq_ser.end()
}
}
pub struct TupleStructSerializer<'a> {
ser: &'a mut Serializer,
elements: Vec<Value>,
}
impl<'a> ser::SerializeTupleStruct for TupleStructSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let toon_value = to_toon_value(value)?;
self.elements.push(toon_value);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
let seq_ser = SeqSerializer {
ser: self.ser,
elements: self.elements,
};
seq_ser.end()
}
}
pub struct TupleVariantSerializer<'a> {
ser: &'a mut Serializer,
variant: String,
elements: Vec<Value>,
}
impl<'a> ser::SerializeTupleVariant for TupleVariantSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let toon_value = to_toon_value(value)?;
self.elements.push(toon_value);
Ok(())
}
fn end(self) -> Result<Self::Ok> {
self.ser.output.push_str(&self.variant);
self.ser.output.push(':');
if self.ser.options.pretty {
self.ser.output.push(' ');
}
let seq_ser = SeqSerializer {
ser: self.ser,
elements: self.elements,
};
seq_ser.end()
}
}
pub struct MapSerializer<'a> {
ser: &'a mut Serializer,
entries: Vec<(String, Value)>,
current_key: Option<String>,
}
impl<'a> ser::SerializeMap for MapSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let key_value = to_toon_value(key)?;
match key_value {
Value::String(s) => {
self.current_key = Some(s);
Ok(())
}
_ => Err(Error::custom("Map keys must be strings")),
}
}
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let key = self
.current_key
.take()
.ok_or_else(|| Error::custom("serialize_value called without serialize_key"))?;
let toon_value = to_toon_value(value)?;
self.entries.push((key, toon_value));
Ok(())
}
fn end(self) -> Result<Self::Ok> {
write_object(
&mut self.ser.output,
&self.entries,
&self.ser.options,
self.ser.indent_level,
);
Ok(())
}
}
pub struct StructSerializer<'a> {
ser: &'a mut Serializer,
entries: Vec<(String, Value)>,
}
impl<'a> ser::SerializeStruct for StructSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let toon_value = to_toon_value(value)?;
self.entries.push((key.to_string(), toon_value));
Ok(())
}
fn end(self) -> Result<Self::Ok> {
write_object(
&mut self.ser.output,
&self.entries,
&self.ser.options,
self.ser.indent_level,
);
Ok(())
}
}
pub struct StructVariantSerializer<'a> {
ser: &'a mut Serializer,
variant: String,
entries: Vec<(String, Value)>,
}
impl<'a> ser::SerializeStructVariant for StructVariantSerializer<'a> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let toon_value = to_toon_value(value)?;
self.entries.push((key.to_string(), toon_value));
Ok(())
}
fn end(self) -> Result<Self::Ok> {
self.ser.output.push_str(&self.variant);
self.ser.output.push(':');
if self.ser.options.pretty {
self.ser.write_newline();
self.ser.indent_level += 1;
}
write_object(
&mut self.ser.output,
&self.entries,
&self.ser.options,
self.ser.indent_level,
);
if self.ser.options.pretty {
self.ser.indent_level -= 1;
}
Ok(())
}
}
pub struct ValueSerializer;
pub struct SerializeVec {
vec: Vec<Value>,
}
pub struct SerializeMap {
map: ToonMap,
current_key: Option<String>,
}
impl ser::Serializer for ValueSerializer {
type Ok = Value;
type Error = Error;
type SerializeSeq = SerializeVec;
type SerializeTuple = SerializeVec;
type SerializeTupleStruct = SerializeVec;
type SerializeTupleVariant = SerializeVec;
type SerializeMap = SerializeMap;
type SerializeStruct = SerializeMap;
type SerializeStructVariant = SerializeMap;
fn serialize_bool(self, v: bool) -> Result<Value> {
Ok(Value::Bool(v))
}
fn serialize_i8(self, v: i8) -> Result<Value> {
Ok(Value::Number(Number::Integer(v as i64)))
}
fn serialize_i16(self, v: i16) -> Result<Value> {
Ok(Value::Number(Number::Integer(v as i64)))
}
fn serialize_i32(self, v: i32) -> Result<Value> {
Ok(Value::Number(Number::Integer(v as i64)))
}
fn serialize_i64(self, v: i64) -> Result<Value> {
Ok(Value::Number(Number::Integer(v)))
}
fn serialize_u8(self, v: u8) -> Result<Value> {
Ok(Value::Number(Number::Integer(v as i64)))
}
fn serialize_u16(self, v: u16) -> Result<Value> {
Ok(Value::Number(Number::Integer(v as i64)))
}
fn serialize_u32(self, v: u32) -> Result<Value> {
Ok(Value::Number(Number::Integer(v as i64)))
}
fn serialize_u64(self, v: u64) -> Result<Value> {
if v <= i64::MAX as u64 {
Ok(Value::Number(Number::Integer(v as i64)))
} else {
Ok(Value::Number(Number::Float(v as f64)))
}
}
fn serialize_f32(self, v: f32) -> Result<Value> {
Ok(Value::Number(Number::Float(v as f64)))
}
fn serialize_f64(self, v: f64) -> Result<Value> {
Ok(Value::Number(Number::Float(v)))
}
fn serialize_char(self, v: char) -> Result<Value> {
Ok(Value::String(v.to_string()))
}
fn serialize_str(self, v: &str) -> Result<Value> {
Ok(Value::String(v.to_string()))
}
fn serialize_bytes(self, v: &[u8]) -> Result<Value> {
let vec = v
.iter()
.map(|&b| Value::Number(Number::Integer(b as i64)))
.collect();
Ok(Value::Array(vec))
}
fn serialize_none(self) -> Result<Value> {
Ok(Value::Null)
}
fn serialize_some<T>(self, value: &T) -> Result<Value>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Value> {
Ok(Value::Null)
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value> {
Ok(Value::Null)
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Value> {
Ok(Value::String(variant.to_string()))
}
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Value>
where
T: ?Sized + Serialize,
{
Err(Error::unsupported_type("newtype variants"))
}
fn serialize_seq(self, _len: Option<usize>) -> Result<SerializeVec> {
Ok(SerializeVec::new())
}
fn serialize_tuple(self, _len: usize) -> Result<SerializeVec> {
Ok(SerializeVec::new())
}
fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<SerializeVec> {
Ok(SerializeVec::new())
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<SerializeVec> {
Err(Error::unsupported_type("tuple variants"))
}
fn serialize_map(self, _len: Option<usize>) -> Result<SerializeMap> {
Ok(SerializeMap::new())
}
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<SerializeMap> {
Ok(SerializeMap::new())
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<SerializeMap> {
Err(Error::unsupported_type("struct variants"))
}
}
impl SerializeVec {
fn new() -> Self {
SerializeVec { vec: Vec::new() }
}
}
impl SerializeMap {
fn new() -> Self {
SerializeMap {
map: ToonMap::new(),
current_key: None,
}
}
}
impl ser::SerializeSeq for SerializeVec {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.vec.push(to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Array(self.vec))
}
}
impl ser::SerializeTuple for SerializeVec {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.vec.push(to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Array(self.vec))
}
}
impl ser::SerializeTupleStruct for SerializeVec {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.vec.push(to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Array(self.vec))
}
}
impl ser::SerializeTupleVariant for SerializeVec {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.vec.push(to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Array(self.vec))
}
}
impl ser::SerializeMap for SerializeMap {
type Ok = Value;
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
match to_toon_value(key)? {
Value::String(s) => {
self.current_key = Some(s);
Ok(())
}
_ => Err(Error::custom("Map keys must be strings")),
}
}
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let key = self
.current_key
.take()
.ok_or_else(|| Error::custom("serialize_value called without serialize_key"))?;
self.map.insert(key, to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Object(self.map))
}
}
impl ser::SerializeStruct for SerializeMap {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.map.insert(key.to_string(), to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Object(self.map))
}
}
impl ser::SerializeStructVariant for SerializeMap {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.map.insert(key.to_string(), to_toon_value(value)?);
Ok(())
}
fn end(self) -> Result<Value> {
Ok(Value::Object(self.map))
}
}
fn to_toon_value<T: Serialize + ?Sized>(value: &T) -> Result<Value> {
value.serialize(ValueSerializer)
}
fn can_be_tabular(elements: &[Value]) -> Option<(Vec<String>, Vec<Vec<Value>>)> {
if elements.is_empty() {
return None;
}
let first_headers = match &elements[0] {
Value::Object(obj) => {
for value in obj.values() {
if !is_primitive_value(value) {
return None;
}
}
let mut headers: Vec<_> = obj.keys().cloned().collect();
headers.sort(); headers
}
_ => return None,
};
let mut rows = Vec::new();
for element in elements {
match element {
Value::Object(obj) => {
let mut element_headers: Vec<_> = obj.keys().cloned().collect();
element_headers.sort();
if element_headers != first_headers {
return None;
}
for value in obj.values() {
if !is_primitive_value(value) {
return None;
}
}
let row: Vec<_> = first_headers
.iter()
.map(|key| obj.get(key).cloned().unwrap_or(Value::Null))
.collect();
rows.push(row);
}
_ => return None,
}
}
Some((first_headers, rows))
}
#[inline]
fn is_primitive_value(value: &Value) -> bool {
match value {
Value::Null
| Value::Bool(_)
| Value::Number(_)
| Value::String(_)
| Value::Date(_)
| Value::BigInt(_) => true,
Value::Array(_) | Value::Object(_) | Value::Table { .. } => false,
}
}
fn write_tabular_array(
output: &mut String,
headers: &[String],
rows: &[Vec<Value>],
options: &ToonOptions,
indent_level: usize,
) {
let delimiter_str = options.delimiter.as_str();
let len_marker = if let Some(marker) = options.length_marker {
format!("{}{}", marker, rows.len())
} else {
rows.len().to_string()
};
let header_suffix = match options.delimiter {
crate::Delimiter::Comma => "", crate::Delimiter::Tab => " ", crate::Delimiter::Pipe => "|",
};
let headers_str = match options.delimiter {
crate::Delimiter::Comma => headers.join(","),
crate::Delimiter::Tab => headers.join(" "), crate::Delimiter::Pipe => headers.join("|"),
};
output.push_str(&format!(
"[{}{}]{{{}}}:",
len_marker, header_suffix, headers_str
));
for row in rows {
output.push('\n');
output.push_str(&" ".repeat((indent_level + 1) * options.indent));
for (i, value) in row.iter().enumerate() {
if i > 0 {
output.push_str(delimiter_str);
}
write_toon_value_quoted(output, value, options);
}
}
}
fn write_inline_array(output: &mut String, elements: &[Value], options: &ToonOptions) {
let delimiter_str = options.delimiter.as_str();
let len_marker = if let Some(marker) = options.length_marker {
format!("{}{}", marker, elements.len())
} else {
elements.len().to_string()
};
let header_suffix = match options.delimiter {
crate::Delimiter::Comma => "",
crate::Delimiter::Tab => " ",
crate::Delimiter::Pipe => "|",
};
output.push_str(&format!("[{}{}]: ", len_marker, header_suffix));
for (i, element) in elements.iter().enumerate() {
if i > 0 {
output.push_str(delimiter_str);
}
write_toon_value_quoted(output, element, options);
}
}
fn write_list_array(
output: &mut String,
elements: &[Value],
options: &ToonOptions,
indent_level: usize,
) {
let len_marker = if let Some(marker) = options.length_marker {
format!("{}{}", marker, elements.len())
} else {
elements.len().to_string()
};
output.push_str(&format!("[{}]:", len_marker));
for element in elements {
output.push('\n');
output.push_str(&" ".repeat((indent_level + 1) * options.indent));
output.push_str("- ");
match element {
Value::Object(obj) => {
let mut sorted_entries: Vec<_> = obj.iter().collect();
sorted_entries.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
let mut iter = sorted_entries.into_iter();
if let Some((first_key, first_value)) = iter.next() {
output.push_str(first_key);
output.push_str(": ");
write_toon_value_quoted(output, first_value, options);
for (key, value) in iter {
output.push('\n');
output.push_str(&" ".repeat((indent_level + 1) * options.indent));
output.push_str(" "); output.push_str(key);
output.push_str(": ");
write_toon_value_quoted(output, value, options);
}
}
}
_ => {
write_toon_value_quoted(output, element, options);
}
}
}
}
fn write_array_toon(
output: &mut String,
arr: &[Value],
options: &ToonOptions,
indent_level: usize,
) {
if arr.is_empty() {
output.push_str("[0]:");
return;
}
if let Some((headers, rows)) = can_be_tabular(arr) {
write_tabular_array(output, &headers, &rows, options, indent_level);
} else if arr.iter().all(is_primitive_value) {
write_inline_array(output, arr, options);
} else {
write_list_array(output, arr, options, indent_level);
}
}
fn write_object(
output: &mut String,
entries: &[(String, Value)],
options: &ToonOptions,
indent_level: usize,
) {
for (i, (key, value)) in entries.iter().enumerate() {
if i > 0 {
output.push('\n');
}
if indent_level > 0 {
output.push_str(&" ".repeat(indent_level * options.indent));
} else if i > 0 && options.pretty {
output.push_str(&" ".repeat(indent_level * options.indent));
}
output.push_str(key);
output.push(':');
match value {
Value::Array(arr) => {
output.push(' ');
write_array_toon(output, arr, options, indent_level);
}
Value::Object(obj) => {
output.push('\n');
let entries: Vec<_> = obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
write_object(output, &entries, options, indent_level + 1);
}
Value::Table { .. } => {
output.push('\n');
output.push_str(&" ".repeat((indent_level + 1) * options.indent));
write_toon_value_quoted(output, value, options);
}
_ => {
output.push(' ');
write_toon_value_quoted(output, value, options);
}
}
}
}
fn write_toon_value_quoted(output: &mut String, value: &Value, options: &ToonOptions) {
match value {
Value::Null => output.push_str("null"),
Value::Bool(b) => output.push_str(if *b { "true" } else { "false" }),
Value::Number(n) => output.push_str(&n.to_string()),
Value::String(s) => {
if needs_quotes_toon(s, options) {
output.push('"');
for ch in s.chars() {
match ch {
'"' => output.push_str("\\\""),
'\\' => output.push_str("\\\\"),
'\n' => output.push_str("\\n"),
'\r' => output.push_str("\\r"),
'\t' => output.push_str("\\t"),
'\u{0008}' => output.push_str("\\b"), '\u{000C}' => output.push_str("\\f"), '\0' => output.push_str("\\0"),
_ => output.push(ch),
}
}
output.push('"');
} else {
output.push_str(s);
}
}
Value::Array(arr) => {
output.push('[');
for (i, elem) in arr.iter().enumerate() {
if i > 0 {
output.push_str(options.delimiter.as_str());
}
write_toon_value_quoted(output, elem, options);
}
output.push(']');
}
Value::Object(obj) => {
let entries: Vec<_> = obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
write_object(output, &entries, options, 0);
}
Value::Table { headers, rows } => {
write_tabular_array(output, headers, rows, options, 0);
}
Value::Date(dt) => {
let s = dt.to_rfc3339();
if needs_quotes_toon(&s, options) {
output.push('"');
output.push_str(&s);
output.push('"');
} else {
output.push_str(&s);
}
}
Value::BigInt(bi) => {
let s = format!("{}n", bi);
if needs_quotes_toon(&s, options) {
output.push('"');
output.push_str(&s);
output.push('"');
} else {
output.push_str(&s);
}
}
}
}
fn needs_quotes_toon(s: &str, options: &ToonOptions) -> bool {
if s.is_empty() {
return true;
}
if s.starts_with(' ') || s.ends_with(' ') {
return true;
}
let active_delimiter = options.delimiter.as_str();
if s.contains(':')
|| s.contains('"')
|| s.contains('\\')
|| s.contains('\n')
|| s.contains('\r')
|| s.contains('\t')
|| s.contains('\0')
{
return true;
}
if s.contains(active_delimiter) {
return true;
}
if s == "true" || s == "false" || s == "null" {
return true;
}
if s.parse::<f64>().is_ok() || s.parse::<i64>().is_ok() {
return true;
}
if s.starts_with("- ") {
return true;
}
if s.starts_with('[') && s.contains(']') {
return true;
}
if s.starts_with('{') && s.contains('}') {
return true;
}
false
}