use crate::JsonEvent;
use std::io::{Error, ErrorKind, Result, Write};
#[cfg(feature = "async-tokio")]
use tokio::io::{AsyncWrite, AsyncWriteExt};
pub struct WriterJsonSerializer<W: Write> {
write: W,
writer: LowLevelJsonSerializer,
}
impl<W: Write> WriterJsonSerializer<W> {
pub const fn new(write: W) -> Self {
Self {
write,
writer: LowLevelJsonSerializer::new(),
}
}
pub fn serialize_event(&mut self, event: JsonEvent<'_>) -> Result<()> {
self.writer.serialize_event(event, &mut self.write)
}
#[deprecated(note = "Use serialize_event() instead")]
pub fn write_event(&mut self, event: JsonEvent<'_>) -> Result<()> {
self.serialize_event(event)
}
pub fn finish(self) -> Result<W> {
self.writer.validate_eof()?;
Ok(self.write)
}
}
#[cfg(feature = "async-tokio")]
pub struct TokioAsyncWriterJsonSerializer<W: AsyncWrite + Unpin> {
write: W,
writer: LowLevelJsonSerializer,
buffer: Vec<u8>,
}
#[cfg(feature = "async-tokio")]
impl<W: AsyncWrite + Unpin> TokioAsyncWriterJsonSerializer<W> {
pub const fn new(write: W) -> Self {
Self {
write,
writer: LowLevelJsonSerializer::new(),
buffer: Vec::new(),
}
}
pub async fn serialize_event(&mut self, event: JsonEvent<'_>) -> Result<()> {
self.writer.serialize_event(event, &mut self.buffer)?;
self.write.write_all(&self.buffer).await?;
self.buffer.clear();
Ok(())
}
#[deprecated(note = "Use serialize_event() instead")]
pub async fn write_event(&mut self, event: JsonEvent<'_>) -> Result<()> {
self.serialize_event(event).await
}
pub fn finish(self) -> Result<W> {
self.writer.validate_eof()?;
Ok(self.write)
}
}
#[derive(Default)]
pub struct LowLevelJsonSerializer {
state_stack: Vec<JsonState>,
element_written: bool,
}
impl LowLevelJsonSerializer {
pub const fn new() -> Self {
Self {
state_stack: Vec::new(),
element_written: false,
}
}
pub fn serialize_event(&mut self, event: JsonEvent<'_>, mut write: impl Write) -> Result<()> {
match event {
JsonEvent::String(s) => {
self.before_value(&mut write)?;
write_escaped_json_string(&s, write)
}
JsonEvent::Number(number) => {
self.before_value(&mut write)?;
write.write_all(number.as_bytes())
}
JsonEvent::Boolean(b) => {
self.before_value(&mut write)?;
write.write_all(if b { b"true" } else { b"false" })
}
JsonEvent::Null => {
self.before_value(&mut write)?;
write.write_all(b"null")
}
JsonEvent::StartArray => {
self.before_value(&mut write)?;
self.state_stack.push(JsonState::OpenArray);
write.write_all(b"[")
}
JsonEvent::EndArray => match self.state_stack.pop() {
Some(JsonState::OpenArray) | Some(JsonState::ContinuationArray) => {
write.write_all(b"]")
}
Some(s) => {
self.state_stack.push(s);
Err(Error::new(
ErrorKind::InvalidInput,
"Closing a not opened array",
))
}
None => Err(Error::new(
ErrorKind::InvalidInput,
"Closing a not opened array",
)),
},
JsonEvent::StartObject => {
self.before_value(&mut write)?;
self.state_stack.push(JsonState::OpenObject);
write.write_all(b"{")
}
JsonEvent::EndObject => match self.state_stack.pop() {
Some(JsonState::OpenObject) | Some(JsonState::ContinuationObject) => {
write.write_all(b"}")
}
Some(s) => {
self.state_stack.push(s);
Err(Error::new(
ErrorKind::InvalidInput,
"Closing a not opened object",
))
}
None => Err(Error::new(
ErrorKind::InvalidInput,
"Closing a not opened object",
)),
},
JsonEvent::ObjectKey(key) => {
match self.state_stack.pop() {
Some(JsonState::OpenObject) => (),
Some(JsonState::ContinuationObject) => write.write_all(b",")?,
_ => {
return Err(Error::new(
ErrorKind::InvalidInput,
"Trying to write an object key in an not object",
))
}
}
self.state_stack.push(JsonState::ContinuationObject);
self.state_stack.push(JsonState::ObjectValue);
write_escaped_json_string(&key, &mut write)?;
write.write_all(b":")
}
JsonEvent::Eof => Err(Error::new(
ErrorKind::InvalidInput,
"EOF is not allowed in JSON writer",
)),
}
}
#[deprecated(note = "Use serialize_event() instead")]
pub fn write_event(&mut self, event: JsonEvent<'_>, write: impl Write) -> Result<()> {
self.serialize_event(event, write)
}
fn before_value(&mut self, mut write: impl Write) -> Result<()> {
match self.state_stack.pop() {
Some(JsonState::OpenArray) => {
self.state_stack.push(JsonState::ContinuationArray);
Ok(())
}
Some(JsonState::ContinuationArray) => {
self.state_stack.push(JsonState::ContinuationArray);
write.write_all(b",")?;
Ok(())
}
Some(last_state @ JsonState::OpenObject)
| Some(last_state @ JsonState::ContinuationObject) => {
self.state_stack.push(last_state);
Err(Error::new(
ErrorKind::InvalidInput,
"Object key expected, string found",
))
}
Some(JsonState::ObjectValue) => Ok(()),
None => {
if self.element_written {
Err(Error::new(
ErrorKind::InvalidInput,
"A root JSON value has already been written",
))
} else {
self.element_written = true;
Ok(())
}
}
}
}
fn validate_eof(&self) -> Result<()> {
if !self.state_stack.is_empty() {
return Err(Error::new(
ErrorKind::InvalidInput,
"The written JSON is not balanced: an object or an array has not been closed",
));
}
if !self.element_written {
return Err(Error::new(
ErrorKind::InvalidInput,
"A JSON file can't be empty",
));
}
Ok(())
}
}
enum JsonState {
OpenArray,
ContinuationArray,
OpenObject,
ContinuationObject,
ObjectValue,
}
fn write_escaped_json_string(s: &str, mut write: impl Write) -> Result<()> {
write.write_all(b"\"")?;
let mut buffer = [b'\\', b'u', 0, 0, 0, 0];
for c in s.chars() {
match c {
'\\' => write.write_all(b"\\\\"),
'"' => write.write_all(b"\\\""),
c => {
if c < char::from(32) {
match c {
'\x08' => write.write_all(b"\\b"),
'\x0C' => write.write_all(b"\\f"),
'\n' => write.write_all(b"\\n"),
'\r' => write.write_all(b"\\r"),
'\t' => write.write_all(b"\\t"),
c => {
let mut c = c as u8;
for i in (2..6).rev() {
let ch = c % 16;
buffer[i] = if ch < 10 { b'0' + ch } else { b'a' + ch - 10 };
c /= 16;
}
write.write_all(&buffer)
}
}
} else {
write.write_all(c.encode_utf8(&mut buffer[2..]).as_bytes())
}
}
}?;
}
write.write_all(b"\"")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_serialization() -> Result<()> {
let events = [
JsonEvent::StartArray,
JsonEvent::StartObject,
JsonEvent::ObjectKey("f\to".into()),
JsonEvent::String("\0\x08\x0c\n\r\t\"\\".into()),
JsonEvent::EndObject,
JsonEvent::Number("112".into()),
JsonEvent::Boolean(true),
JsonEvent::Boolean(false),
JsonEvent::Null,
JsonEvent::EndArray,
];
let mut serializer = WriterJsonSerializer::new(Vec::new());
for event in events {
serializer.serialize_event(event)?;
}
assert_eq!(
serializer.finish()?,
br#"[{"f\to":"\u0000\b\f\n\r\t\"\\"},112,true,false,null]"#
);
Ok(())
}
}