use encode::combinators::Separated;
use encode::Encodable;
use encode::EncodableSize;
use encode::StrEncoder;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum Json {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Json>),
Object(HashMap<String, Json>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JsonString<S>(pub S);
impl<S: AsRef<str>, E: StrEncoder> Encodable<E> for JsonString<S> {
type Error = E::Error;
fn encode(&self, encoder: &mut E) -> Result<(), Self::Error> {
let s = self.0.as_ref();
'"'.encode(encoder)?;
for c in s.chars() {
match c {
'"' => r#"\""#.encode(encoder)?,
'\\' => r#"\\"#.encode(encoder)?,
'\x08' => r#"\b"#.encode(encoder)?,
'\x0c' => r#"\f"#.encode(encoder)?,
'\n' => r#"\n"#.encode(encoder)?,
'\r' => r#"\r"#.encode(encoder)?,
'\t' => r#"\t"#.encode(encoder)?,
c if c.is_control() => format_args!("\\u{:04x}", c as u32).encode(encoder)?,
c => c.encode(encoder)?,
}
}
'"'.encode(encoder)
}
}
impl<E: StrEncoder> Encodable<E> for Json {
type Error = E::Error;
#[inline]
fn encode(&self, encoder: &mut E) -> Result<(), Self::Error> {
match self {
Json::Null => "null".encode(encoder),
Json::Bool(true) => "true".encode(encoder),
Json::Bool(false) => "false".encode(encoder),
Json::Number(n) => format_args!("{n}").encode(encoder),
Json::String(s) => JsonString(s).encode(encoder),
Json::Array(a) => ('[', Separated::new(a, ','), ']').encode(encoder),
Json::Object(o) => (
'{',
Separated::new(o.iter().map(|(k, v)| (JsonString(k), ':', v)), ','),
'}',
)
.encode(encoder),
}
}
}
impl std::fmt::Display for Json {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.encode(f)
}
}
fn main() -> Result<(), Box<dyn core::error::Error>> {
let json = Json::Object(HashMap::from([
("name".into(), Json::String("John Doe".into())),
("age".into(), Json::Number(42.0)),
("is_student".into(), Json::Bool(false)),
(
"grades".into(),
Json::Array(vec![
Json::Number(100.0),
Json::Number(90.0),
Json::Number(80.0),
]),
),
("weird \n string\\\0".into(), Json::Null),
]));
let size = json.encoded_size()?;
println!("Expected JSON size: {}", size);
let mut s = String::with_capacity(size);
json.encode(&mut s)?;
println!("{s}");
assert_eq!(
s.len(),
size,
"The size of the encoded JSON string should match the calculated size"
);
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn assert_booleans_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Bool(true).encode(&mut buf).unwrap();
assert_eq!(&buf, b"true");
buf.clear();
Json::Bool(false).encode(&mut buf).unwrap();
assert_eq!(&buf, b"false");
}
#[test]
fn assert_numbers_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Number(42.0).encode(&mut buf).unwrap();
assert_eq!(&buf, b"42");
}
#[test]
fn assert_strings_are_encoded_correctly() {
let mut buf = Vec::new();
Json::String("Hello, World!".into())
.encode(&mut buf)
.unwrap();
let s = String::from_utf8(buf).unwrap();
assert_eq!(s, "\"Hello, World!\"");
}
#[test]
fn assert_arrays_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Array(vec![
Json::Number(1.0),
Json::Number(2.0),
Json::Number(3.0),
])
.encode(&mut buf)
.unwrap();
assert_eq!(&buf, b"[1,2,3]");
}
#[test]
fn assert_empty_arrays_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Array(vec![]).encode(&mut buf).unwrap();
assert_eq!(&buf, b"[]");
}
#[test]
fn assert_objects_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Object(HashMap::from([(
"name".into(),
Json::String("John Doe".into()),
)]))
.encode(&mut buf)
.unwrap();
assert_eq!(&buf, b"{\"name\":\"John Doe\"}");
}
#[test]
fn assert_empty_objects_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Object(HashMap::new()).encode(&mut buf).unwrap();
assert_eq!(&buf, b"{}");
}
#[test]
fn assert_nulls_are_encoded_correctly() {
let mut buf = Vec::new();
Json::Null.encode(&mut buf).unwrap();
assert_eq!(&buf, b"null");
}
}