use serde::{Deserialize, Serialize};
use crate::utils::write_indent;
pub(crate) type ValueList = Vec<Value>;
#[cfg(not(feature = "preserve-order"))]
pub(crate) type ValueMap = std::collections::BTreeMap<String, Value>;
#[cfg(feature = "preserve-order")]
pub(crate) type ValueMap = indexmap::IndexMap<String, Value>;
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
#[default]
Null,
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
List(ValueList),
Map(ValueMap),
}
impl Value {
pub(crate) fn format<W: std::fmt::Write>(
&self,
writer: &mut W,
pretty: bool,
indent_level: usize,
) -> std::fmt::Result {
match self {
Value::Null => write!(writer, "null"),
Value::String(s) => write!(writer, "\"{}\"", s),
Value::Integer(i) => write!(writer, "{}", i),
Value::Float(f) => {
if f.fract() == 0.0 {
write!(writer, "{:.1}", f)
} else {
write!(writer, "{f}")
}
}
Value::Boolean(b) => write!(writer, "{}", b),
Value::List(l) => {
write!(writer, "[")?;
if l.is_empty() {
write!(writer, "]")?;
return Ok(());
}
if pretty {
writeln!(writer)?;
write_indent(writer, indent_level + 1)?;
}
let mut list_iter = l.iter().peekable();
while let Some(value) = list_iter.next() {
value.format(writer, pretty, indent_level + 1)?;
if list_iter.peek().is_some() {
write!(writer, ",")?;
if pretty {
writeln!(writer)?;
write_indent(writer, indent_level + 1)?
} else {
write!(writer, " ")?;
}
}
}
if pretty {
writeln!(writer)?;
write_indent(writer, indent_level)?;
}
write!(writer, "]")
}
Value::Map(m) => {
write!(writer, "[")?;
if m.is_empty() {
write!(writer, "]")?;
return Ok(());
}
if pretty {
writeln!(writer)?;
write_indent(writer, indent_level + 1)?;
}
let mut map_iter = m.iter().peekable();
while let Some((key, value)) = map_iter.next() {
write!(writer, "\"{key}\": ")?;
value.format(writer, pretty, indent_level + 1)?;
if map_iter.peek().is_some() {
write!(writer, ",")?;
if pretty {
writeln!(writer)?;
write_indent(writer, indent_level + 1)?
} else {
write!(writer, " ")?;
}
}
}
if pretty {
writeln!(writer)?;
write_indent(writer, indent_level)?;
}
write!(writer, "]")
}
}
}
pub fn write_formatted<W: std::fmt::Write>(
&self,
writer: &mut W,
pretty: bool,
) -> std::fmt::Result {
self.format(writer, pretty, 0)
}
pub fn is_string(&self) -> bool {
matches!(self, Value::String(_))
}
pub fn is_integer(&self) -> bool {
matches!(self, Value::Integer(_))
}
pub fn is_float(&self) -> bool {
matches!(self, Value::Float(_))
}
pub fn is_boolean(&self) -> bool {
matches!(self, Value::Boolean(_))
}
pub fn is_list(&self) -> bool {
matches!(self, Value::List(_))
}
pub fn is_map(&self) -> bool {
matches!(self, Value::Map(_))
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.format(f, false, 0).map_err(|_| std::fmt::Error)
}
}