use std::{
io::{ErrorKind, Write},
str::Utf8Error,
};
use super::*;
use crate::utf8;
#[derive(Clone, Debug)]
pub struct WriterSettings {
pub pretty_print: bool,
pub escape_all_control_chars: bool,
pub escape_all_non_ascii: bool,
pub multi_top_level_value_separator: Option<String>,
}
impl Default for WriterSettings {
fn default() -> Self {
WriterSettings {
pretty_print: false,
escape_all_control_chars: false,
escape_all_non_ascii: false,
multi_top_level_value_separator: None,
}
}
}
#[derive(PartialEq, Debug)]
enum StackValue {
Array,
Object,
}
#[derive(Debug)]
struct Writer<W: Write>(W);
impl<W: Write> Writer<W> {
fn write(&mut self, bytes: &[u8]) -> Result<(), IoError> {
self.0.write_all(bytes)
}
fn flush(&mut self) -> Result<(), IoError> {
self.0.flush()
}
}
#[derive(Debug)]
pub struct JsonStreamWriter<W: Write> {
writer: Writer<W>,
is_empty: bool,
expects_member_name: bool,
stack: Vec<StackValue>,
is_string_value_writer_active: bool,
writer_settings: WriterSettings,
}
impl<W: Write> JsonStreamWriter<W> {
pub fn new(writer: W) -> Self {
JsonStreamWriter::new_custom(writer, WriterSettings::default())
}
pub fn new_custom(writer: W, writer_settings: WriterSettings) -> Self {
Self {
writer: Writer(writer),
is_empty: true,
expects_member_name: false,
stack: Vec::with_capacity(16),
is_string_value_writer_active: false,
writer_settings,
}
}
pub fn finish_document_no_flush(self) -> Result<<Self as JsonWriter>::WriterResult, IoError> {
self.on_finish();
Ok(self.writer.0)
}
}
impl<W: Write> JsonStreamWriter<W> {
fn is_in_array(&self) -> bool {
self.stack.last() == Some(&StackValue::Array)
}
fn is_in_object(&self) -> bool {
self.stack.last() == Some(&StackValue::Object)
}
fn write_indentation(&mut self) -> Result<(), IoError> {
let indentation_level = self.stack.len();
for _ in 0..indentation_level {
self.writer.write(b" ")?;
}
Ok(())
}
fn before_container_element(&mut self) -> Result<(), IoError> {
if !self.is_empty {
self.writer.write(b",")?;
}
if self.writer_settings.pretty_print {
self.writer.write(b"\n")?;
self.write_indentation()?;
}
Ok(())
}
fn before_value(&mut self) -> Result<(), IoError> {
if self.is_string_value_writer_active {
panic!(
"Incorrect writer usage: Cannot finish document when string value writer is still active"
);
}
if self.expects_member_name {
panic!("Incorrect writer usage: Cannot write value when name is expected");
}
let is_top_level = self.stack.is_empty();
if is_top_level && !self.is_empty {
match &self.writer_settings.multi_top_level_value_separator {
None => panic!(
"Incorrect writer usage: Cannot write multiple top-level values when not enabled in writer settings"
),
Some(separator) => {
self.writer.write(separator.as_bytes())?;
}
}
} else if self.is_in_array() {
self.before_container_element()?;
}
self.is_empty = false;
if self.is_in_object() {
self.expects_member_name = true;
}
Ok(())
}
fn on_container_start(&mut self, container_type: StackValue) -> Result<(), IoError> {
self.before_value()?;
self.stack.push(container_type);
self.is_empty = true;
Ok(())
}
fn on_container_end(&mut self) -> Result<(), IoError> {
self.stack.pop();
if !self.is_empty && self.writer_settings.pretty_print {
self.writer.write(b"\n")?;
self.write_indentation()?;
}
self.is_empty = false;
self.expects_member_name = self.is_in_object();
Ok(())
}
fn on_finish(&self) {
if self.is_string_value_writer_active {
panic!(
"Incorrect writer usage: Cannot finish document when string value writer is still active"
);
}
if self.expects_member_name {
panic!("Incorrect writer usage: Cannot finish document when member name is expected");
}
if self.stack.is_empty() {
if self.is_empty {
panic!(
"Incorrect writer usage: Cannot finish document when no value has been written yet"
);
}
} else {
panic!(
"Incorrect writer usage: Cannot finish document when top-level value is not finished"
);
}
}
}
impl<W: Write> JsonStreamWriter<W> {
fn should_escape(&self, c: char) -> bool {
matches!(c, '"' | '\\')
|| matches!(c, '\u{0}'..='\u{1F}')
|| (self.writer_settings.escape_all_non_ascii && !c.is_ascii())
|| (self.writer_settings.escape_all_control_chars && c.is_control())
}
fn write_escaped_char(&mut self, c: char) -> Result<(), IoError> {
fn get_unicode_escape(value: u32) -> [u8; 4] {
debug_assert!(value <= u16::MAX as u32);
fn to_hex(i: u32) -> u8 {
match i {
0..=9 => b'0' + i as u8,
10..=15 => b'A' + (i - 10) as u8,
_ => unreachable!("Unexpected value {i}"),
}
}
[
to_hex((value >> 12) & 15),
to_hex((value >> 8) & 15),
to_hex((value >> 4) & 15),
to_hex(value & 15),
]
}
let escape = match c {
'"' => "\\\"",
'\\' => "\\\\",
'/' => "\\/",
'\u{0008}' => "\\b",
'\u{000C}' => "\\f",
'\n' => "\\n",
'\r' => "\\r",
'\t' => "\\t",
'\0'..='\u{FFFF}' => {
self.writer.write(b"\\u")?;
self.writer.write(&get_unicode_escape(c as u32))?;
return Ok(());
}
_ => {
let temp = (c as u32) - 0x10000;
let high = (temp >> 10) + 0xD800;
let low = (temp & ((1 << 10) - 1)) + 0xDC00;
self.writer.write(b"\\u")?;
self.writer.write(&get_unicode_escape(high))?;
self.writer.write(b"\\u")?;
self.writer.write(&get_unicode_escape(low))?;
return Ok(());
}
};
self.writer.write(escape.as_bytes())
}
fn write_string_value_piece(&mut self, value: &str) -> Result<(), IoError> {
let bytes = value.as_bytes();
let mut next_to_write_index = 0;
for (index, char) in value.char_indices() {
if self.should_escape(char) {
if index > next_to_write_index {
self.writer.write(&bytes[next_to_write_index..index])?;
}
self.write_escaped_char(char)?;
next_to_write_index = index + char.len_utf8();
}
}
if next_to_write_index < bytes.len() {
self.writer.write(&bytes[next_to_write_index..])?;
}
Ok(())
}
fn write_string_value(&mut self, value: &str) -> Result<(), IoError> {
self.writer.write(b"\"")?;
self.write_string_value_piece(value)?;
self.writer.write(b"\"")
}
}
impl<W: Write> JsonWriter for JsonStreamWriter<W> {
type WriterResult = W;
fn begin_object(&mut self) -> Result<(), IoError> {
self.on_container_start(StackValue::Object)?;
self.expects_member_name = true;
self.writer.write(b"{")
}
fn name(&mut self, name: &str) -> Result<(), IoError> {
if !self.expects_member_name {
panic!("Incorrect writer usage: Cannot write name when name is not expected");
}
if self.is_string_value_writer_active {
panic!(
"Incorrect writer usage: Cannot finish document when string value writer is still active"
);
}
self.before_container_element()?;
self.write_string_value(name)?;
self.writer.write(b":")?;
if self.writer_settings.pretty_print {
self.writer.write(b" ")?;
}
self.expects_member_name = false;
Ok(())
}
fn end_object(&mut self) -> Result<(), IoError> {
if !self.is_in_object() {
panic!("Incorrect writer usage: Cannot end object when not inside object");
}
if self.is_string_value_writer_active {
panic!(
"Incorrect writer usage: Cannot end object when string value writer is still active"
);
}
if !self.expects_member_name {
panic!("Incorrect writer usage: Cannot end object when member value is expected");
}
self.on_container_end()?;
self.writer.write(b"}")
}
fn begin_array(&mut self) -> Result<(), IoError> {
self.on_container_start(StackValue::Array)?;
self.expects_member_name = false;
self.writer.write(b"[")
}
fn end_array(&mut self) -> Result<(), IoError> {
if !self.is_in_array() {
panic!("Incorrect writer usage: Cannot end array when not inside array");
}
if self.is_string_value_writer_active {
panic!(
"Incorrect writer usage: Cannot end array when string value writer is still active"
);
}
self.on_container_end()?;
self.writer.write(b"]")
}
fn string_value(&mut self, value: &str) -> Result<(), IoError> {
self.before_value()?;
self.write_string_value(value)
}
fn bool_value(&mut self, value: bool) -> Result<(), IoError> {
self.before_value()?;
self.writer.write(if value { b"true" } else { b"false" })
}
fn null_value(&mut self) -> Result<(), IoError> {
self.before_value()?;
self.writer.write(b"null")
}
fn number_value<N: FiniteNumber>(&mut self, value: N) -> Result<(), IoError> {
value.use_json_number(|number_str| {
self.before_value()?;
self.writer.write(number_str.as_bytes())
})
}
fn fp_number_value<N: FloatingPointNumber>(&mut self, value: N) -> Result<(), JsonNumberError> {
value.use_json_number(|number_str| {
self.before_value()?;
self.writer.write(number_str.as_bytes())
})
}
fn number_value_from_string(&mut self, value: &str) -> Result<(), JsonNumberError> {
if is_valid_json_number(value) {
self.before_value()?;
self.writer.write(value.as_bytes())?;
Ok(())
} else {
Err(JsonNumberError::InvalidNumber(format!(
"invalid JSON number: {value}"
)))
}
}
#[cfg(feature = "serde")]
fn serialize_value<S: serde_core::ser::Serialize>(
&mut self,
value: &S,
) -> Result<(), crate::serde::SerializerError> {
let mut serializer = crate::serde::JsonWriterSerializer::new(self);
value.serialize(&mut serializer)
}
fn finish_document(mut self) -> Result<Self::WriterResult, IoError> {
self.on_finish();
self.writer.flush()?;
Ok(self.writer.0)
}
fn string_value_writer(&mut self) -> Result<impl StringValueWriter + '_, IoError> {
self.before_value()?;
self.writer.write(b"\"")?;
self.is_string_value_writer_active = true;
Ok(StringValueWriterImpl {
json_writer: self,
utf8_buf: [0_u8; utf8::MAX_BYTES_PER_CHAR],
utf8_pos: 0,
utf8_expected_len: 0,
error: None,
})
}
}
struct StringValueWriterImpl<'j, W: Write> {
json_writer: &'j mut JsonStreamWriter<W>,
utf8_buf: [u8; utf8::MAX_BYTES_PER_CHAR],
utf8_pos: usize,
utf8_expected_len: usize,
error: Option<(ErrorKind, String)>,
}
fn map_utf8_error(e: Utf8Error) -> IoError {
IoError::new(ErrorKind::InvalidData, e)
}
fn decode_utf8_char(bytes: &[u8]) -> Result<&str, IoError> {
match std::str::from_utf8(bytes) {
Err(e) => Err(map_utf8_error(e)),
Ok(s) => {
debug_assert!(s.chars().count() == 1);
Ok(s)
}
}
}
impl<W: Write> StringValueWriterImpl<'_, W> {
fn write_impl(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
let mut start_pos = 0;
if self.utf8_pos > 0 {
let copy_count = (self.utf8_expected_len - self.utf8_pos).min(buf.len());
self.utf8_buf[self.utf8_pos..(self.utf8_pos + copy_count)]
.copy_from_slice(&buf[..copy_count]);
self.utf8_pos += copy_count;
if self.utf8_pos >= self.utf8_expected_len {
self.utf8_pos = 0;
let s = decode_utf8_char(&self.utf8_buf[..self.utf8_expected_len])?;
self.json_writer.write_string_value_piece(s)?;
}
start_pos += copy_count;
}
fn max_or_offset_negative(a: usize, b: usize, b_neg_off: usize) -> usize {
debug_assert!(b >= a);
if b_neg_off > b { a } else { b - b_neg_off }
}
let mut i = max_or_offset_negative(start_pos, buf.len(), utf8::MAX_BYTES_PER_CHAR);
while i < buf.len() {
let byte = buf[i];
if !utf8::is_1byte(byte) {
let expected_bytes_count;
if utf8::is_2byte_start(byte) {
expected_bytes_count = 2;
} else if utf8::is_3byte_start(byte) {
expected_bytes_count = 3;
} else if utf8::is_4byte_start(byte) {
expected_bytes_count = 4;
} else if utf8::is_continuation(byte) {
i += 1;
continue;
} else {
return Err(IoError::new(ErrorKind::InvalidData, "invalid UTF-8 data"));
}
let remaining_count = buf.len() - i;
if remaining_count < expected_bytes_count {
self.json_writer.write_string_value_piece(
std::str::from_utf8(&buf[start_pos..i]).map_err(map_utf8_error)?,
)?;
self.utf8_expected_len = expected_bytes_count;
self.utf8_pos = remaining_count;
self.utf8_buf[..remaining_count].copy_from_slice(&buf[i..]);
return Ok(buf.len());
} else {
i += expected_bytes_count - 1;
}
}
i += 1;
}
self.json_writer.write_string_value_piece(
std::str::from_utf8(&buf[start_pos..]).map_err(map_utf8_error)?,
)?;
Ok(buf.len())
}
fn check_previous_error(&self) -> std::io::Result<()> {
match &self.error {
None => Ok(()),
Some(e) => Err(IoError::other(format!(
"previous error '{}': {}",
e.0,
e.1.clone()
))),
}
}
fn run_with_error_tracking<T>(
&mut self,
f: impl FnOnce(&mut Self) -> Result<T, IoError>,
) -> Result<T, IoError> {
self.check_previous_error()?;
let result = f(self);
if let Err(e) = &result {
self.error = Some((e.kind(), e.to_string()));
}
result
}
}
impl<W: Write> Write for StringValueWriterImpl<'_, W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.run_with_error_tracking(|self_| self_.write_impl(buf))
}
fn flush(&mut self) -> std::io::Result<()> {
self.run_with_error_tracking(|self_| self_.json_writer.writer.flush())
}
}
impl<W: Write> StringValueWriter for StringValueWriterImpl<'_, W> {
fn write_str(&mut self, s: &str) -> Result<(), IoError> {
self.run_with_error_tracking(|self_| {
if self_.utf8_pos > 0 {
return Err(IoError::new(
ErrorKind::InvalidData,
"incomplete multi-byte UTF-8 data",
));
}
self_.json_writer.write_string_value_piece(s)
})
}
fn finish_value(self) -> Result<(), IoError> {
self.check_previous_error()?;
if self.utf8_pos > 0 {
return Err(IoError::new(
ErrorKind::InvalidData,
"incomplete multi-byte UTF-8 data",
));
}
self.json_writer.writer.write(b"\"")?;
self.json_writer.is_string_value_writer_active = false;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{cell::Cell, cmp::min};
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn numbers() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array()?;
json_writer.number_value(8_u8)?;
json_writer.number_value(-8_i8)?;
json_writer.number_value(16_u16)?;
json_writer.number_value(-16_i16)?;
json_writer.number_value(32_u32)?;
json_writer.number_value(-32_i32)?;
json_writer.number_value(64_u64)?;
json_writer.number_value(-64_i64)?;
json_writer.number_value(128_u128)?;
json_writer.number_value(-128_i128)?;
json_writer.fp_number_value(1.5_f32)?;
json_writer.fp_number_value(-1.5_f32)?;
json_writer.fp_number_value(2.5_f64)?;
json_writer.fp_number_value(-2.5_f64)?;
json_writer.number_value_from_string("123.45e-12")?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!(
"[8,-8,16,-16,32,-32,64,-64,128,-128,1.5,-1.5,2.5,-2.5,123.45e-12]",
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn numbers_invalid() {
fn assert_invalid_number(result: Result<(), JsonNumberError>, expected_message: &str) {
match result {
Ok(_) => panic!("Should have failed"),
Err(e) => match e {
JsonNumberError::InvalidNumber(message) => {
assert_eq!(expected_message, message)
}
JsonNumberError::IoError(e) => panic!("Unexpected error: {e:?}"),
},
}
}
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
assert_invalid_number(
json_writer.fp_number_value(f32::NAN),
&format!("non-finite number: {}", f32::NAN),
);
assert_invalid_number(
json_writer.fp_number_value(f64::INFINITY),
&format!("non-finite number: {}", f64::INFINITY),
);
assert_invalid_number(
json_writer.number_value_from_string("NaN"),
"invalid JSON number: NaN",
);
assert_invalid_number(
json_writer.number_value_from_string("+1"),
"invalid JSON number: +1",
);
assert_invalid_number(
json_writer.number_value_from_string("00"),
"invalid JSON number: 00",
);
assert_invalid_number(
json_writer.number_value_from_string("1e"),
"invalid JSON number: 1e",
);
assert_invalid_number(
json_writer.number_value_from_string("12a"),
"invalid JSON number: 12a",
);
}
#[test]
fn literals() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array()?;
json_writer.bool_value(true)?;
json_writer.bool_value(false)?;
json_writer.null_value()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!("[true,false,null]", String::from_utf8(writer)?);
Ok(())
}
#[test]
fn arrays() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array()?;
json_writer.begin_array()?;
json_writer.number_value(1)?;
json_writer.end_array()?;
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!("[[1],[]]", String::from_utf8(writer)?);
Ok(())
}
#[test]
#[should_panic(expected = "Incorrect writer usage: Cannot end array when not inside array")]
fn end_array_not_in_array() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object().unwrap();
let _ = json_writer.end_array();
}
#[test]
fn objects() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object()?;
json_writer.name("a")?;
json_writer.number_value(1)?;
json_writer.name("")?;
json_writer.number_value(2)?;
json_writer.name("a")?;
json_writer.begin_object()?;
json_writer.name("c")?;
json_writer.begin_object()?;
json_writer.end_object()?;
json_writer.end_object()?;
json_writer.end_object()?;
json_writer.finish_document()?;
assert_eq!(r#"{"a":1,"":2,"a":{"c":{}}}"#, String::from_utf8(writer)?);
Ok(())
}
#[test]
#[should_panic(expected = "Incorrect writer usage: Cannot end object when not inside object")]
fn end_object_not_in_object() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array().unwrap();
let _ = json_writer.end_object();
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot end object when member value is expected"
)]
fn end_object_expecting_value() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object().unwrap();
json_writer.name("a").unwrap();
let _ = json_writer.end_object();
}
#[test]
fn arrays_objects_mixed() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object()?;
json_writer.name("a")?;
json_writer.begin_object()?;
json_writer.name("b")?;
json_writer.begin_array()?;
json_writer.begin_object()?;
json_writer.end_object()?;
json_writer.begin_object()?;
json_writer.name("c")?;
json_writer.begin_array()?;
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.end_array()?;
json_writer.end_object()?;
json_writer.end_array()?;
json_writer.name("d")?;
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.end_object()?;
json_writer.end_object()?;
json_writer.finish_document()?;
assert_eq!(
r#"{"a":{"b":[{},{"c":[[]]}],"d":[]}}"#,
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn strings() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array()?;
json_writer.string_value("")?;
json_writer.string_value("ab")?;
json_writer.string_value("\u{0000}\u{001F}")?;
json_writer.string_value("a b")?;
json_writer.string_value("\"\\/\u{0008}\u{000C}\n\r\t")?;
json_writer.string_value("\u{10FFFF}")?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!(
r#"["","ab","\u0000\u001F","a b","\"\\/\b\f\n\r\t","#.to_owned() + "\"\u{10FFFF}\"]",
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn string_writer() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array()?;
let mut string_writer = json_writer.string_value_writer()?;
assert_eq!(0, string_writer.write(b"")?);
string_writer.write_all(b"a b")?;
string_writer.write_all(b"\x00\x1F")?;
string_writer.write_all(b"\"\\/\x08\x0C\n\r\t")?;
string_writer.write_all("\u{007F}\u{10FFFF}".as_bytes())?;
let bytes = "\u{007F}\u{0080}\u{07FF}\u{0800}\u{FFFF}\u{10000}\u{10FFFF}".as_bytes();
for b in bytes {
string_writer.write_all(&[*b])?;
}
string_writer.finish_value()?;
let mut string_writer = json_writer.string_value_writer()?;
string_writer.write_all("\u{10FFFF}".as_bytes())?;
string_writer.write_str("a \u{10FFFF}")?;
string_writer.write_all("b".as_bytes())?;
string_writer.write_str("")?; string_writer.write_all("c".as_bytes())?;
string_writer.finish_value()?;
let string_writer = json_writer.string_value_writer()?;
string_writer.finish_value()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!(
r#"["a b\u0000\u001F\"\\/\b\f\n\r\t"#.to_owned()
+ "\u{007F}\u{10FFFF}\u{007F}\u{0080}\u{07FF}\u{0800}\u{FFFF}\u{10000}\u{10FFFF}\",\"\u{10FFFF}a \u{10FFFF}bc\",\"\"]",
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn string_writer_flush() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
let mut string_writer = json_writer.string_value_writer()?;
string_writer.write_all(b"abcd")?;
string_writer.flush()?;
string_writer.write_all(b"efgh")?;
string_writer.flush()?;
drop(string_writer);
assert_eq!("\"abcdefgh", String::from_utf8(writer)?);
Ok(())
}
#[test]
fn string_writer_invalid() {
macro_rules! assert_invalid_utf8 {
(
|$string_value_writer:ident| $writing_expr:expr,
$expected_custom_message:expr, // Option<&str>
) => {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
let mut $string_value_writer = json_writer.string_value_writer().unwrap();
#[allow(unused_mut, reason = "only for some callers of the macro the closure has to be mutable")]
let mut writing_function = || -> Result<(), IoError> {
$writing_expr
};
let expected_custom_message: Option<&str> = $expected_custom_message;
match writing_function() {
Ok(_) => panic!("Should have failed"),
Err(e) => {
assert_eq!(ErrorKind::InvalidData, e.kind());
match expected_custom_message {
None => assert!(
e.get_ref().unwrap().is::<Utf8Error>(),
"Inner error is not Utf8Error"
),
Some(message) => {
assert_eq!(message, e.to_string(), "Custom message does not match")
}
}
}
}
}
}
assert_invalid_utf8!(
|w| {
w.write_all(b"\xF8")
},
Some("invalid UTF-8 data"),
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\xED\xA0\x80")
},
None,
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\xF4")?;
w.write_all(b"\x90")?;
w.write_all(b"\x80")?;
w.write_all(b"\x80")
},
None,
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\xC1\xBF")
},
None,
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\xF0")?;
w.write_all(b"\x90")?;
w.write_all(b"\x80")?;
w.finish_value()
},
Some("incomplete multi-byte UTF-8 data"),
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\x80")?;
w.finish_value()
},
None,
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\xC2")?;
w.write_all(b"\x80")?;
w.write_all(b"\x80")?;
w.finish_value()
},
None,
);
assert_invalid_utf8!(
|w| {
w.write_all(b"\xF0")?;
w.write_str("")?; w.finish_value()
},
Some("incomplete multi-byte UTF-8 data"),
);
}
#[test]
fn string_writer_repeats_error() {
macro_rules! test_error_handling {
($writer:expr, |$string_writer:ident| $failing_action:expr, $expected_error_kind:expr, $expected_message:expr) => {{
let mut json_writer = JsonStreamWriter::new($writer);
let mut $string_writer = json_writer.string_value_writer().unwrap();
let result = $failing_action;
match result {
Ok(_) => panic!("Should have failed"),
Err(e) => {
assert_eq!($expected_error_kind, e.kind());
let wrapped_error = e.get_ref().unwrap();
assert_eq!($expected_message, wrapped_error.to_string());
}
}
fn assert_error(result: std::io::Result<()>) {
match result {
Ok(_) => panic!("Should have failed"),
Err(e) => {
assert_eq!(ErrorKind::Other, e.kind());
let wrapped_error = e.get_ref().unwrap();
assert_eq!(
format!(
"previous error '{}': {}",
$expected_error_kind, $expected_message
),
wrapped_error.to_string()
);
}
}
}
assert_error($string_writer.write_all(b"test"));
assert_error($string_writer.write_str("test"));
assert_error($string_writer.flush());
assert_error($string_writer.finish_value());
assert!(json_writer.is_string_value_writer_active);
}};
}
test_error_handling!(
std::io::sink(),
|string_writer| {
string_writer.write_all(b"\xF8")
},
ErrorKind::InvalidData,
"invalid UTF-8 data"
);
struct MaxCapacityWriter {
remaining_capacity: usize,
}
impl Write for MaxCapacityWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if self.remaining_capacity == 0 {
return Err(IoError::new(ErrorKind::WouldBlock, "custom-error"));
}
let write_count = min(self.remaining_capacity, buf.len());
self.remaining_capacity -= write_count;
Ok(write_count)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
test_error_handling!(
MaxCapacityWriter {
remaining_capacity: 2,
},
|string_writer| { string_writer.write_str("test") },
ErrorKind::WouldBlock,
"custom-error"
);
struct FlushErrorWriter;
impl Write for FlushErrorWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Err(std::io::Error::new(ErrorKind::WouldBlock, "custom-error"))
}
}
test_error_handling!(
FlushErrorWriter,
|string_writer| { string_writer.flush() },
ErrorKind::WouldBlock,
"custom-error"
);
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot end array when string value writer is still active"
)]
fn string_writer_array_incomplete() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array().unwrap();
let string_writer = json_writer.string_value_writer().unwrap();
drop(string_writer);
let _ = json_writer.end_array();
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot end object when string value writer is still active"
)]
fn string_writer_object_incomplete() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object().unwrap();
json_writer.name("a").unwrap();
let string_writer = json_writer.string_value_writer().unwrap();
drop(string_writer);
let _ = json_writer.end_object();
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot finish document when string value writer is still active"
)]
fn string_writer_incomplete_top_level() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
let string_writer = json_writer.string_value_writer().unwrap();
drop(string_writer);
let _ = json_writer.finish_document();
}
#[test]
#[should_panic(expected = "Incorrect writer usage: Cannot write value when name is expected")]
fn string_writer_for_name() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object().unwrap();
let _ = json_writer.string_value_writer();
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot write name when name is not expected"
)]
fn name_as_value() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
let _ = json_writer.name("test");
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot write name when name is not expected"
)]
fn name_as_member_value() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object().unwrap();
json_writer.name("a").unwrap();
let _ = json_writer.name("test");
}
#[test]
fn multiple_top_level() -> TestResult {
fn create_writer<W: Write>(writer: W, top_level_separator: &str) -> JsonStreamWriter<W> {
JsonStreamWriter::new_custom(
writer,
WriterSettings {
multi_top_level_value_separator: Some(top_level_separator.to_owned()),
..Default::default()
},
)
}
let mut writer = Vec::<u8>::new();
let mut json_writer = create_writer(&mut writer, "");
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!("[]", String::from_utf8(writer)?);
let mut writer = Vec::<u8>::new();
let mut json_writer = create_writer(&mut writer, "");
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!("[][]", String::from_utf8(writer)?);
let mut writer = Vec::<u8>::new();
let mut json_writer = create_writer(&mut writer, "#\n#");
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!("[]#\n#[]", String::from_utf8(writer)?);
Ok(())
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot write multiple top-level values when not enabled in writer settings"
)]
fn multiple_top_level_disallowed() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.bool_value(true).unwrap();
let _ = json_writer.bool_value(false);
}
#[test]
fn pretty_print() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new_custom(
&mut writer,
WriterSettings {
pretty_print: true,
multi_top_level_value_separator: Some("#".to_owned()),
..Default::default()
},
);
json_writer.begin_array()?;
json_writer.begin_array()?;
json_writer.end_array()?;
json_writer.begin_array()?;
json_writer.number_value(1)?;
json_writer.end_array()?;
json_writer.begin_object()?;
json_writer.name("a")?;
json_writer.begin_array()?;
json_writer.begin_object()?;
json_writer.name("b")?;
json_writer.number_value(2)?;
json_writer.end_object()?;
json_writer.begin_object()?;
json_writer.end_object()?;
json_writer.end_array()?;
json_writer.name("c")?;
json_writer.number_value(3)?;
json_writer.end_object()?;
json_writer.end_array()?;
json_writer.begin_array()?;
json_writer.number_value(4)?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!(
"[\n [],\n [\n 1\n ],\n {\n \"a\": [\n {\n \"b\": 2\n },\n {}\n ],\n \"c\": 3\n }\n]#[\n 4\n]",
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn escape_all_control_chars() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new_custom(
&mut writer,
WriterSettings {
escape_all_control_chars: true,
..Default::default()
},
);
json_writer
.string_value("\u{0000}\u{001F} test \" \u{007E}\u{007F}\u{0080}\u{009F}\u{00A0}")?;
json_writer.finish_document()?;
assert_eq!(
"\"\\u0000\\u001F test \\\" \u{007E}\\u007F\\u0080\\u009F\u{00A0}\"",
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn escape_all_non_ascii() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new_custom(
&mut writer,
WriterSettings {
escape_all_non_ascii: true,
..Default::default()
},
);
json_writer.string_value("\u{0000}\u{001F} test \" \u{007F}\u{0080}\u{10000}\u{10FFFF}")?;
json_writer.finish_document()?;
assert_eq!(
"\"\\u0000\\u001F test \\\" \u{007F}\\u0080\\uD800\\uDC00\\uDBFF\\uDFFF\"",
String::from_utf8(writer)?
);
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new_custom(
&mut writer,
WriterSettings {
escape_all_control_chars: true,
escape_all_non_ascii: true,
..Default::default()
},
);
json_writer.string_value("\u{0000} test \" \u{007F}\u{0080}\u{10FFFF}")?;
json_writer.finish_document()?;
assert_eq!(
"\"\\u0000 test \\\" \\u007F\\u0080\\uDBFF\\uDFFF\"",
String::from_utf8(writer)?
);
Ok(())
}
#[duplicate::duplicate_item(
method;
[finish_document];
[finish_document_no_flush];
)]
mod method {
use super::*;
#[test]
fn result() -> TestResult {
let mut json_writer = JsonStreamWriter::new(Vec::<u8>::new());
json_writer.string_value("text")?;
let written_bytes = json_writer.method()?;
assert_eq!("\"text\"", String::from_utf8(written_bytes)?);
Ok(())
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot finish document when no value has been written yet"
)]
fn empty_document() {
let mut writer = Vec::<u8>::new();
let json_writer = JsonStreamWriter::new(&mut writer);
let _ = json_writer.method();
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot finish document when top-level value is not finished"
)]
fn incomplete_document() {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array().unwrap();
let _ = json_writer.method();
}
}
#[test]
fn finish_document_flush() -> TestResult {
struct FlushTrackingWriter<'a> {
was_flushed: &'a Cell<bool>,
}
impl Write for FlushTrackingWriter<'_> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
self.was_flushed.set(true);
Ok(())
}
}
let was_flushed = Cell::new(false);
let mut writer = FlushTrackingWriter {
was_flushed: &was_flushed,
};
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.bool_value(true)?;
assert!(!was_flushed.get());
json_writer.finish_document()?;
assert!(was_flushed.get());
let was_flushed = Cell::new(false);
let mut writer = FlushTrackingWriter {
was_flushed: &was_flushed,
};
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.bool_value(true)?;
assert!(!was_flushed.get());
json_writer.finish_document_no_flush()?;
assert!(!was_flushed.get());
Ok(())
}
struct InterruptedWriter {
buf: Vec<u8>,
interrupted_count: u32,
}
impl InterruptedWriter {
pub fn new() -> Self {
InterruptedWriter {
buf: Vec::new(),
interrupted_count: 0,
}
}
pub fn get_written_string(self) -> String {
String::from_utf8(self.buf).unwrap()
}
}
impl Write for InterruptedWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
if self.interrupted_count >= 3 {
self.interrupted_count = 0;
self.buf.push(buf[0]);
Ok(1)
} else {
self.interrupted_count += 1;
Err(IoError::from(ErrorKind::Interrupted))
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn string_writer_interrupted() -> TestResult {
let mut writer = InterruptedWriter::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
let mut string_writer = json_writer.string_value_writer()?;
let string_bytes = "test \" \u{10FFFF}".as_bytes();
match string_writer.write(string_bytes) {
Ok(n) => assert_eq!(string_bytes.len(), n),
r => panic!("Unexpected result: {r:?}"),
}
string_writer.finish_value()?;
json_writer.finish_document()?;
assert_eq!("\"test \\\" \u{10FFFF}\"", writer.get_written_string());
Ok(())
}
#[test]
fn writer_interrupted() -> TestResult {
let mut writer = InterruptedWriter::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_array()?;
json_writer.bool_value(true)?;
json_writer.number_value_from_string("123.4e5")?;
json_writer.string_value("test \" 1 \u{10FFFF}")?;
let mut string_writer = json_writer.string_value_writer()?;
string_writer.write_all("test \" 2 \u{10FFFF}, ".as_bytes())?;
string_writer.write_str("test \" 3 \u{10FFFF}")?;
string_writer.finish_value()?;
json_writer.end_array()?;
json_writer.finish_document()?;
assert_eq!(
"[true,123.4e5,\"test \\\" 1 \u{10FFFF}\",\"test \\\" 2 \u{10FFFF}, test \\\" 3 \u{10FFFF}\"]",
writer.get_written_string()
);
Ok(())
}
#[cfg(feature = "serde")]
mod serde {
use super::*;
use crate::serde::SerializerError;
use ::serde_core::{Serialize, Serializer, ser::SerializeStruct};
use std::collections::HashMap;
#[test]
fn serialize_value() -> TestResult {
let mut writer = Vec::<u8>::new();
let mut json_writer = JsonStreamWriter::new(&mut writer);
json_writer.begin_object()?;
json_writer.name("outer")?;
struct CustomStruct;
impl Serialize for CustomStruct {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut struc = serializer.serialize_struct("name", 3)?;
struc.serialize_field("a", &1)?;
struc.serialize_field("b", &HashMap::from([("key", "value")]))?;
struc.serialize_field("c", &[1, 2])?;
struc.end()
}
}
json_writer.serialize_value(&CustomStruct)?;
json_writer.end_object()?;
json_writer.finish_document()?;
assert_eq!(
r#"{"outer":{"a":1,"b":{"key":"value"},"c":[1,2]}}"#,
String::from_utf8(writer)?
);
Ok(())
}
#[test]
fn serialize_value_invalid() {
let mut json_writer = JsonStreamWriter::new(std::io::sink());
let number = f32::NAN;
match json_writer.serialize_value(&number) {
Err(SerializerError::InvalidNumber(message)) => {
assert_eq!(format!("non-finite number: {number}"), message);
}
r => panic!("Unexpected result: {r:?}"),
}
}
#[test]
#[should_panic(
expected = "Incorrect writer usage: Cannot write value when name is expected"
)]
fn serialize_value_no_value_expected() {
let mut json_writer = JsonStreamWriter::new(std::io::sink());
json_writer.begin_object().unwrap();
let _ = json_writer.serialize_value(&"test");
}
}
}