#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
Integer(i128),
Float(f64),
String(String),
Bytes(Vec<u8>),
Array(Vec<Value>),
Object(Vec<(String, Value)>),
}
impl Value {
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Integer(i) => (*i).try_into().ok(),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Value::Array(arr) => Some(arr.as_slice()),
_ => None,
}
}
pub fn as_object(&self) -> Option<&[(String, Value)]> {
match self {
Value::Object(obj) => Some(obj.as_slice()),
_ => None,
}
}
pub fn emit<E: crate::codec::FormatEmitter + ?Sized>(
&self,
emitter: &E,
dest: &mut dyn crate::io::IDestination,
) -> Result<(), crate::error::BabbelError> {
emitter.emit(self, dest)
}
pub fn emit_pretty<E: crate::codec::FormatEmitter + ?Sized>(
&self,
emitter: &E,
dest: &mut dyn crate::io::IDestination,
indent: usize,
) -> Result<(), crate::error::BabbelError> {
emitter.emit_pretty(self, dest, indent)
}
pub fn serialize_json(&self, dest: &mut dyn crate::io::IDestination) {
match self {
Value::Null => dest.add_bytes("null"),
Value::Bool(b) => dest.add_bytes(if *b { "true" } else { "false" }),
Value::Integer(i) => {
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(*i));
}
Value::Float(f) => {
let mut buf = dtoa::Buffer::new();
dest.add_bytes(buf.format(*f));
}
Value::String(s) => {
crate::escape::write_json_escaped_string(s, dest);
}
Value::Bytes(bytes) => {
crate::escape::write_json_escaped_string(&alloc::string::String::from_utf8_lossy(bytes), dest);
}
Value::Array(items) => {
dest.add_bytes("[");
for (idx, item) in items.iter().enumerate() {
if idx > 0 {
dest.add_bytes(",");
}
item.serialize_json(dest);
}
dest.add_bytes("]");
}
Value::Object(entries) => {
dest.add_bytes("{");
for (idx, (k, v)) in entries.iter().enumerate() {
if idx > 0 {
dest.add_bytes(",");
}
crate::escape::write_json_escaped_string(k, dest);
dest.add_bytes(":");
v.serialize_json(dest);
}
dest.add_bytes("}");
}
}
}
pub fn serialize_yaml(&self, dest: &mut dyn crate::io::IDestination, indent: usize) {
match self {
Value::Null => dest.add_bytes("null"),
Value::Bool(b) => dest.add_bytes(if *b { "true" } else { "false" }),
Value::Integer(i) => {
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(*i));
}
Value::Float(f) => {
let mut buf = dtoa::Buffer::new();
dest.add_bytes(buf.format(*f));
}
Value::String(s) => {
if s.contains('\n') || s.contains('"') {
dest.add_bytes("|\n");
for line in s.lines() {
for _ in 0..(indent + 2) {
dest.add_byte(b' ');
}
dest.add_bytes(line);
dest.add_bytes("\n");
}
} else {
dest.add_bytes(s);
}
}
Value::Bytes(b) => {
dest.add_bytes(&alloc::string::String::from_utf8_lossy(b));
}
Value::Array(items) => {
if items.is_empty() {
dest.add_bytes("[]");
return;
}
dest.add_bytes("\n");
for item in items {
for _ in 0..indent {
dest.add_byte(b' ');
}
dest.add_bytes("- ");
item.serialize_yaml(dest, indent + 2);
dest.add_bytes("\n");
}
}
Value::Object(entries) => {
if entries.is_empty() {
dest.add_bytes("{}");
return;
}
dest.add_bytes("\n");
for (k, v) in entries {
for _ in 0..indent {
dest.add_byte(b' ');
}
dest.add_bytes(k);
dest.add_bytes(": ");
v.serialize_yaml(dest, indent + 2);
dest.add_bytes("\n");
}
}
}
}
pub fn serialize_bencode(&self, dest: &mut dyn crate::io::IDestination) {
match self {
Value::Null => {}
Value::Bool(b) => {
dest.add_bytes(if *b { "i1e" } else { "i0e" });
}
Value::Integer(i) => {
dest.add_bytes("i");
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(*i));
dest.add_bytes("e");
}
Value::Float(f) => {
let mut buf = itoa::Buffer::new();
dest.add_bytes("i");
let rounded = if *f >= 0.0 { (*f + 0.5) as i64 } else { (*f - 0.5) as i64 };
dest.add_bytes(buf.format(rounded));
dest.add_bytes("e");
}
Value::String(s) => {
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(s.len()));
dest.add_bytes(":");
dest.add_bytes(s);
}
Value::Bytes(b) => {
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(b.len()));
dest.add_bytes(":");
if let Ok(s) = core::str::from_utf8(b) {
dest.add_bytes(s);
} else {
for &byte in b {
dest.add_byte(byte);
}
}
}
Value::Array(items) => {
dest.add_bytes("l");
for item in items {
item.serialize_bencode(dest);
}
dest.add_bytes("e");
}
Value::Object(entries) => {
dest.add_bytes("d");
for (k, v) in entries {
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(k.len()));
dest.add_bytes(":");
dest.add_bytes(k);
v.serialize_bencode(dest);
}
dest.add_bytes("e");
}
}
}
pub fn serialize_xml(&self, dest: &mut dyn crate::io::IDestination, root_tag: Option<&str>) {
let tag = root_tag.unwrap_or("root");
match self {
Value::Null => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes("/>");
}
Value::Bool(b) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
dest.add_bytes(if *b { "true" } else { "false" });
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
Value::Integer(i) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
let mut buf = itoa::Buffer::new();
dest.add_bytes(buf.format(*i));
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
Value::Float(f) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
let mut buf = dtoa::Buffer::new();
dest.add_bytes(buf.format(*f));
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
Value::String(s) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
crate::escape::write_xml_escaped_string(s, dest);
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
Value::Bytes(b) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
crate::escape::write_xml_escaped_string(&alloc::string::String::from_utf8_lossy(b), dest);
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
Value::Array(items) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
for item in items {
item.serialize_xml(dest, Some("item"));
}
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
Value::Object(entries) => {
dest.add_bytes("<");
dest.add_bytes(tag);
dest.add_bytes(">");
for (k, v) in entries {
v.serialize_xml(dest, Some(k));
}
dest.add_bytes("</");
dest.add_bytes(tag);
dest.add_bytes(">");
}
}
}
}
pub trait FormatVisitor {
type Error;
fn visit_null(&mut self) -> Result<(), Self::Error>;
fn visit_bool(&mut self, val: bool) -> Result<(), Self::Error>;
fn visit_integer(&mut self, val: i128) -> Result<(), Self::Error>;
fn visit_float(&mut self, val: f64) -> Result<(), Self::Error>;
fn visit_str(&mut self, val: &str) -> Result<(), Self::Error>;
fn visit_bytes(&mut self, val: &[u8]) -> Result<(), Self::Error>;
fn visit_array_start(&mut self) -> Result<(), Self::Error>;
fn visit_array_end(&mut self) -> Result<(), Self::Error>;
fn visit_object_start(&mut self) -> Result<(), Self::Error>;
fn visit_key(&mut self, key: &str) -> Result<(), Self::Error>;
fn visit_object_end(&mut self) -> Result<(), Self::Error>;
}