use crate::blocking::io::BlockingWrite;
use crate::shared::*;
use core::fmt::Display;
use core::marker::PhantomData;
pub struct JsonWriter <'a, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> {
inner: &'a mut W,
formatter: F,
unreported_error: Option<W::Error>,
_float_format: FF,
}
impl <'a, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> JsonWriter<'a, W, F, FF> {
pub fn new(inner: &'a mut W, formatter: F, float_format: FF) -> JsonWriter<'a, W, F, FF> {
JsonWriter {
inner,
formatter,
unreported_error: None,
_float_format: float_format,
}
}
pub fn write_bytes(&mut self, data: &[u8]) -> Result<(), W::Error> {
self.flush()?;
self.inner.write_all(data)
}
pub fn write_escaped_string(&mut self, s: &str) -> Result<(), W::Error> {
self.write_bytes(b"\"")?;
for b in s.bytes() {
match b {
b'"' => self.write_bytes(b"\\\"")?,
b'\\' => self.write_bytes(b"\\\\")?,
b'\x08' => self.write_bytes(b"\\b")?,
b'\x0c' => self.write_bytes(b"\\f")?,
b'\n' => self.write_bytes(b"\\n")?,
b'\r' => self.write_bytes(b"\\r")?,
b'\t' => self.write_bytes(b"\\t")?,
b if b < 0x20 => {
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
let bytes = &[
b'\\',
b'u',
b'0',
b'0',
HEX_DIGITS[(b >> 4) as usize],
HEX_DIGITS[(b & 0xF) as usize],
];
self.write_bytes(bytes)?
},
b => self.write_bytes(&[b])?,
}
}
self.write_bytes(b"\"")?;
Ok(())
}
pub fn write_bool(&mut self, value: bool) -> Result<(), W::Error> {
if value {
self.write_bytes(b"true")
}
else {
self.write_bytes(b"false")
}
}
pub fn write_f64(&mut self, value: f64) -> Result<(), W::Error> {
FormatWrapper::new(self)
.write_f64(value)
}
pub fn write_f32(&mut self, value: f32) -> Result<(), W::Error> {
FormatWrapper::new(self)
.write_f32(value)
}
pub fn write_raw_num(&mut self, value: impl Display) -> Result<(), W::Error> {
FormatWrapper::new(self)
.write_raw(value)
}
pub fn write_format_after_key(&mut self) -> Result<(), W::Error> {
self.flush()?;
self.inner.write_all(self.formatter.after_key().as_bytes())
}
pub fn write_format_after_start_nested(&mut self) -> Result<(), W::Error> {
self.flush()?;
self.inner.write_all(self.formatter.after_start_nested().as_bytes())
}
pub fn write_format_after_element(&mut self) -> Result<(), W::Error> {
self.flush()?;
self.inner.write_all(self.formatter.after_element().as_bytes())
}
pub fn write_format_before_end_nested(&mut self, is_empty: bool) -> Result<(), W::Error> {
self.flush()?;
self.inner.write_all(self.formatter.before_end_nested(is_empty).as_bytes())
}
pub fn write_format_indent(&mut self) -> Result<(), W::Error> {
self.flush()?;
self.inner.write_all(self.formatter.indent().as_bytes())
}
pub fn flush(&mut self) -> Result<(), W::Error> {
if let Some(e) = self.unreported_error.take() {
return Err(e);
}
Ok(())
}
pub fn into_inner(mut self) -> Result<&'a mut W, W::Error> {
self.flush()?;
Ok(self.inner)
}
pub(crate) fn set_unreported_error(&mut self, unreported_error: W::Error) {
self.unreported_error = Some(unreported_error);
}
}
impl <'a, W: BlockingWrite> JsonWriter<'a, W, CompactFormatter, DefaultFloatFormat> {
pub fn new_compact(inner: &'a mut W) -> Self {
JsonWriter::new(inner, CompactFormatter, DefaultFloatFormat)
}
}
impl <'a, W: BlockingWrite> JsonWriter<'a, W, PrettyFormatter, DefaultFloatFormat> {
pub fn new_pretty(inner: &'a mut W) -> Self {
JsonWriter::new(inner, PrettyFormatter::new(), DefaultFloatFormat)
}
}
struct FormatWrapper<'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> {
inner: &'a mut JsonWriter<'b, W, F, FF>,
cached_error: Option<W::Error>,
pd: PhantomData<FF>,
}
impl<'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> FormatWrapper<'a, 'b, W, F, FF> {
fn new(inner: &'a mut JsonWriter<'b, W, F, FF>) -> Self {
Self {
inner,
cached_error: None,
pd: PhantomData::default(),
}
}
fn write_raw(&mut self, value: impl Display) -> Result<(), W::Error> {
use core::fmt::Write;
let _ = write!(self, "{}", value);
match self.cached_error.take() {
None => Ok(()),
Some(e) => Err(e),
}
}
fn write_f64(&mut self, value: f64) -> Result<(), W::Error> {
let _ = FF::write_f64(self, value);
match self.cached_error.take() {
None => Ok(()),
Some(e) => Err(e),
}
}
fn write_f32(&mut self, value: f32) -> Result<(), W::Error> {
let _ = FF::write_f32(self, value);
match self.cached_error.take() {
None => Ok(()),
Some(e) => Err(e),
}
}
}
impl<'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> core::fmt::Write for FormatWrapper<'a, 'b, W, F, FF> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
match self.inner.write_bytes(s.as_bytes()) {
Ok(_) => {
Ok(())
}
Err(e) => {
self.cached_error = Some(e);
Err(core::fmt::Error)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
use std::fmt::Write;
use std::io;
#[test]
fn test_json_writer() {
_test_json_writer(JsonWriter::new(&mut Vec::new(), CompactFormatter, DefaultFloatFormat));
}
#[test]
fn test_json_writer_compact() {
_test_json_writer(JsonWriter::new_compact(&mut Vec::new()));
}
#[test]
fn test_json_writer_pretty() {
_test_json_writer(JsonWriter::new_pretty(&mut Vec::new()));
}
fn _test_json_writer<F: JsonFormatter>(mut writer: JsonWriter<Vec<u8>, F, DefaultFloatFormat>) {
writer.write_bytes(b"a").unwrap();
writer.write_bytes(b"b").unwrap();
writer.write_bytes(b"cde").unwrap();
writer.flush().unwrap();
assert_eq!(as_written_string(writer), "abcde");
}
fn as_written_string<F: JsonFormatter>(writer: JsonWriter<Vec<u8>, F, DefaultFloatFormat>) -> String {
let s = writer.into_inner().unwrap();
String::from_utf8(s.to_vec()).unwrap()
}
#[rstest]
#[case::empty("", r#""""#)]
#[case::text("yo", r#""yo""#)]
#[case::non_ascii("äöü", r#""äöü""#)]
#[case::quotation_mark("\"", r#""\"""#)]
#[case::backquote("\\", r#""\\""#)]
#[case::backspace("\x08", r#""\b""#)]
#[case::form_feed("\x0c", r#""\f""#)]
#[case::line_feed("\n", r#""\n""#)]
#[case::carriage_return("\r", r#""\r""#)]
#[case::tab("\t", r#""\t""#)]
#[case::esc_00("\x00", r#""\u0000""#)]
#[case::esc_01("\x01", r#""\u0001""#)]
#[case::esc_02("\x02", r#""\u0002""#)]
#[case::esc_03("\x03", r#""\u0003""#)]
#[case::esc_04("\x04", r#""\u0004""#)]
#[case::esc_05("\x05", r#""\u0005""#)]
#[case::esc_06("\x06", r#""\u0006""#)]
#[case::esc_07("\x07", r#""\u0007""#)]
#[case::esc_08("\x08", r#""\b""#)]
#[case::esc_09("\x09", r#""\t""#)]
#[case::esc_0a("\x0a", r#""\n""#)]
#[case::esc_0b("\x0b", r#""\u000b""#)]
#[case::esc_0c("\x0c", r#""\f""#)]
#[case::esc_0d("\x0d", r#""\r""#)]
#[case::esc_0e("\x0e", r#""\u000e""#)]
#[case::esc_0f("\x0f", r#""\u000f""#)]
#[case::esc_10("\x10", r#""\u0010""#)]
#[case::esc_11("\x11", r#""\u0011""#)]
#[case::esc_12("\x12", r#""\u0012""#)]
#[case::esc_13("\x13", r#""\u0013""#)]
#[case::esc_14("\x14", r#""\u0014""#)]
#[case::esc_15("\x15", r#""\u0015""#)]
#[case::esc_16("\x16", r#""\u0016""#)]
#[case::esc_17("\x17", r#""\u0017""#)]
#[case::esc_18("\x18", r#""\u0018""#)]
#[case::esc_19("\x19", r#""\u0019""#)]
#[case::esc_1a("\x1a", r#""\u001a""#)]
#[case::esc_1b("\x1b", r#""\u001b""#)]
#[case::esc_1c("\x1c", r#""\u001c""#)]
#[case::esc_1d("\x1d", r#""\u001d""#)]
#[case::esc_1e("\x1e", r#""\u001e""#)]
#[case::esc_1f("\x1f", r#""\u001f""#)]
#[case::combination("asdf \n jklö \t!", r#""asdf \n jklö \t!""#)]
fn test_write_escaped_string(#[case] s: &str, #[case] expected: &str) {
let mut buf = Vec::new();
let mut writer = JsonWriter::new_compact(&mut buf);
writer.write_escaped_string(s).unwrap();
assert_eq!(as_written_string(writer), expected);
}
#[rstest]
#[case::bool_true(true, "true")]
#[case::bool_false(false, "false")]
fn test_write_bool(#[case] b: bool, #[case] expected: &str) {
let mut buf = Vec::new();
let mut writer = JsonWriter::new_compact(&mut buf);
writer.write_bool(b).unwrap();
assert_eq!(as_written_string(writer), expected);
}
#[rstest]
#[case::simple(2.0, "2")]
#[case::exp_5(1.234e5, "123400")]
#[case::exp_10(1.234e10, "1.234e10")]
#[case::exp_20(1.234e20, "1.234e20")]
#[case::exp_neg_3(1.234e-3, "0.001234")]
#[case::exp_neg_10(1.234e-10, "1.234e-10")]
#[case::neg_simple(-2.0, "-2")]
#[case::neg_exp_5(-1.234e5, "-123400")]
#[case::neg_exp_10(-1.234e10, "-1.234e10")]
#[case::neg_exp_20(-1.234e20, "-1.234e20")]
#[case::neg_exp_neg_3(-1.234e-3, "-0.001234")]
#[case::neg_exp_neg_10(-1.234e-10, "-1.234e-10")]
#[case::inf(f64::INFINITY, "null")]
#[case::neg_inf(f64::NEG_INFINITY, "null")]
#[case::nan(f64::NAN, "null")]
fn test_write_f64(#[case] value: f64, #[case] expected: &str) {
let mut buf = Vec::new();
let mut writer = JsonWriter::new_compact(&mut buf);
writer.write_f64(value).unwrap();
assert_eq!(as_written_string(writer), expected);
}
#[rstest]
#[case::simple(2.0, "2")]
#[case::exp_5(1.234e5, "123400")]
#[case::exp_10(1.234e10, "1.234e10")]
#[case::exp_20(1.234e20, "1.234e20")]
#[case::exp_neg_3(1.234e-3, "0.001234")]
#[case::exp_neg_10(1.234e-10, "1.234e-10")]
#[case::neg_simple(-2.0, "-2")]
#[case::neg_exp_5(-1.234e5, "-123400")]
#[case::neg_exp_10(-1.234e10, "-1.234e10")]
#[case::neg_exp_20(-1.234e20, "-1.234e20")]
#[case::neg_exp_neg_3(-1.234e-3, "-0.001234")]
#[case::neg_exp_neg_10(-1.234e-10, "-1.234e-10")]
#[case::inf(f32::INFINITY, "null")]
#[case::neg_inf(f32::NEG_INFINITY, "null")]
#[case::nan(f32::NAN, "null")]
fn test_write_f32(#[case] value: f32, #[case] expected: &str) {
let mut buf = Vec::new();
let mut writer = JsonWriter::new_compact(&mut buf);
writer.write_f32(value).unwrap();
assert_eq!(as_written_string(writer), expected);
}
#[test]
fn test_set_reported_error() {
let mut buf = Vec::new();
let mut writer = JsonWriter::new_compact(&mut buf);
writer.write_bytes(b"yo").unwrap();
writer.set_unreported_error(io::Error::new(io::ErrorKind::Other, "something went wrong"));
match writer.write_bytes(b" after error") {
Ok(_) => {
panic!("previous error should have been returned");
}
Err(e) => {
assert_eq!(e.kind(), io::ErrorKind::Other);
assert_eq!(e.to_string(), "something went wrong");
},
}
assert_eq!(as_written_string(writer), "yo");
}
#[rstest]
fn test_float_format() {
struct OtherFf;
impl FloatFormat for OtherFf {
fn write_f64(f: &mut impl Write, value: f64) -> std::fmt::Result {
write!(f, "_{}_64", value)
}
fn write_f32(f: &mut impl Write, value: f32) -> std::fmt::Result {
write!(f, "_{}_32", value)
}
}
let mut buf = Vec::new();
let mut writer = JsonWriter::new(&mut buf, CompactFormatter, OtherFf);
writer.write_f64(1.2).unwrap();
writer.write_f32(3.4).unwrap();
assert_eq!(&buf, b"_1.2_64_3.4_32");
}
#[test]
fn test_flush() {
let mut buf = Vec::new();
let mut writer = JsonWriter::new_compact(&mut buf);
writer.write_bytes(b"yo").unwrap();
writer.set_unreported_error(io::Error::new(io::ErrorKind::Other, "something went wrong"));
match writer.flush() {
Ok(_) => {
panic!("previous error should have been returned");
}
Err(e) => {
assert_eq!(e.kind(), io::ErrorKind::Other);
assert_eq!(e.to_string(), "something went wrong");
},
}
assert_eq!(as_written_string(writer), "yo");
}
}