#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::JsonWrite;
use crate::WriteJsonKey;
#[cfg(feature = "alloc")]
pub trait ToJsonValue {
fn to_json_value(&self) -> String;
}
#[cfg(feature = "alloc")]
impl<T> ToJsonValue for T
where
T: WriteJsonValue + ?Sized,
{
fn to_json_value(&self) -> String {
let mut result = String::new();
let _ = self.write_json_value(&mut result);
result
}
}
pub trait WriteJsonValue {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result;
}
impl WriteJsonValue for bool {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for u8 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for i8 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for u16 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for i16 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for u32 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for i32 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for u64 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for i64 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for u128 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for i128 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write!(writer, "{self}")
}
}
impl WriteJsonValue for f32 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
if self.is_nan() || self.is_infinite() {
None::<Self>.write_json_value(writer)
} else {
if self % 1.0 == 0.0 {
write!(writer, "{self}.0")
} else {
write!(writer, "{self}")
}
}
}
}
impl WriteJsonValue for f64 {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
if self.is_nan() || self.is_infinite() {
None::<Self>.write_json_value(writer)
} else {
if self % 1.0 == 0.0 {
write!(writer, "{self}.0")
} else {
write!(writer, "{self}")
}
}
}
}
impl WriteJsonValue for char {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
let mut buf = [0; 4];
let v = self.encode_utf8(&mut buf);
v.write_json_value(writer)
}
}
impl WriteJsonValue for str {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write_json_str(self, writer)
}
}
#[cfg(feature = "alloc")]
impl WriteJsonValue for String {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
self.as_str().write_json_value(writer)
}
}
#[cfg(feature = "alloc")]
impl WriteJsonValue for Cow<'_, str> {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
self.as_ref().write_json_value(writer)
}
}
impl<T: WriteJsonValue> WriteJsonValue for Option<T> {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
match self {
Some(v) => v.write_json_value(writer),
None => write_json_null(writer),
}
}
}
impl<V: WriteJsonValue> WriteJsonValue for [V] {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
writer.open_array()?;
let mut iter = self.iter();
if let Some(v) = iter.next() {
writer.value(v)?;
}
for v in iter {
writer.val_sep()?;
writer.space()?;
writer.value(v)?;
}
writer.close_array()?;
Ok(())
}
}
impl<V: WriteJsonValue, const N: usize> WriteJsonValue for [V; N] {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
self.as_slice().write_json_value(writer)
}
}
#[cfg(feature = "alloc")]
impl<V: WriteJsonValue> WriteJsonValue for Vec<V> {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
self.as_slice().write_json_value(writer)
}
}
#[cfg(feature = "alloc")]
impl<K: WriteJsonKey, V: WriteJsonValue> WriteJsonValue for alloc::collections::BTreeMap<K, V> {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write_json_object(self.iter(), writer)
}
}
#[cfg(feature = "std")]
impl<K: WriteJsonKey, V: WriteJsonValue> WriteJsonValue for std::collections::HashMap<K, V> {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
write_json_object(self.iter(), writer)
}
}
impl<V: WriteJsonValue + ?Sized> WriteJsonValue for &V {
fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
(*self).write_json_value(writer)
}
}
pub(crate) fn write_json_null<W: JsonWrite + ?Sized>(writer: &mut W) -> core::fmt::Result {
write!(writer, "null")
}
pub(crate) fn write_json_str<W: JsonWrite + ?Sized>(
value: &str,
writer: &mut W,
) -> core::fmt::Result {
write!(writer, "\"")?;
format_escaped_str_contents(writer, value)?;
write!(writer, "\"")?;
Ok(())
}
fn format_escaped_str_contents<W>(writer: &mut W, value: &str) -> core::fmt::Result
where
W: ?Sized + JsonWrite,
{
let mut bytes = value.as_bytes();
let mut i = 0;
while i < bytes.len() {
let (string_run, rest) = bytes.split_at(i);
let (&byte, rest) = rest.split_first().unwrap();
let escape = ESCAPE[byte as usize];
i += 1;
if escape == 0 {
continue;
}
bytes = rest;
i = 0;
let string_run = unsafe { core::str::from_utf8_unchecked(string_run) };
if !string_run.is_empty() {
write!(writer, "{string_run}")?;
}
let char_escape = match escape {
BB => CharEscape::Backspace,
TT => CharEscape::Tab,
NN => CharEscape::LineFeed,
FF => CharEscape::FormFeed,
RR => CharEscape::CarriageReturn,
QU => CharEscape::Quote,
BS => CharEscape::ReverseSolidus,
UU => CharEscape::AsciiControl(byte),
_ => unsafe { core::hint::unreachable_unchecked() },
};
write_char_escape(writer, char_escape)?;
}
let string_run = unsafe { core::str::from_utf8_unchecked(bytes) };
if string_run.is_empty() {
return Ok(());
}
write!(writer, "{string_run}")?;
Ok(())
}
const BB: u8 = b'b'; const TT: u8 = b't'; const NN: u8 = b'n'; const FF: u8 = b'f'; const RR: u8 = b'r'; const QU: u8 = b'"'; const BS: u8 = b'\\'; const UU: u8 = b'u'; const __: u8 = 0;
static ESCAPE: [u8; 256] = [
UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ];
enum CharEscape {
Quote,
ReverseSolidus,
Backspace,
FormFeed,
LineFeed,
CarriageReturn,
Tab,
AsciiControl(u8),
}
fn write_char_escape<W>(writer: &mut W, char_escape: CharEscape) -> core::fmt::Result
where
W: ?Sized + JsonWrite,
{
let escape_char = match char_escape {
CharEscape::Quote => '"',
CharEscape::ReverseSolidus => '\\',
CharEscape::Backspace => 'b',
CharEscape::FormFeed => 'f',
CharEscape::LineFeed => 'n',
CharEscape::CarriageReturn => 'r',
CharEscape::Tab => 't',
CharEscape::AsciiControl(_) => 'u',
};
match char_escape {
CharEscape::AsciiControl(byte) => {
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
let first = HEX_DIGITS[(byte >> 4) as usize] as char;
let second = HEX_DIGITS[(byte & 0xF) as usize] as char;
write!(writer, "\\{escape_char}00{first}{second}")
}
_ => {
write!(writer, "\\{escape_char}")
}
}
}
fn write_json_object<
'i,
I: Iterator<Item = (&'i K, &'i V)>,
K: WriteJsonKey + 'i,
V: WriteJsonValue + 'i,
W: JsonWrite + ?Sized,
>(
mut iter: I,
writer: &mut W,
) -> core::fmt::Result {
writer.open_object()?;
let mut trailing_space = false;
if let Some((key, value)) = iter.next() {
writer.space()?;
writer.key(key)?;
writer.keyval_sep()?;
writer.space()?;
writer.value(value)?;
trailing_space = true;
}
for (key, value) in iter {
writer.val_sep()?;
writer.space()?;
writer.key(key)?;
writer.keyval_sep()?;
writer.space()?;
writer.value(value)?;
}
if trailing_space {
writer.space()?;
}
writer.close_object()?;
Ok(())
}