pub use serde::{self, Deserialize, Serialize};
pub use serde_json::{Deserializer, Error, Map, Number, Value, json};
pub fn to_string<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
serde_json::to_string(value)
}
pub fn to_string_pretty<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
serde_json::to_string_pretty(value)
}
pub fn to_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, Error> {
serde_json::to_vec(value)
}
pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
writer: W,
value: &T,
) -> Result<(), Error> {
serde_json::to_writer(writer, value)
}
pub fn from_str<'de, T: serde::Deserialize<'de>>(text: &'de str) -> Result<T, Error> {
serde_json::from_str(text)
}
pub fn from_slice<'de, T: serde::Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, Error> {
serde_json::from_slice(bytes)
}
pub fn from_reader<R: std::io::Read, T: serde::de::DeserializeOwned>(
reader: R,
) -> Result<T, Error> {
serde_json::from_reader(reader)
}
pub fn from_value<T: serde::de::DeserializeOwned>(value: Value) -> Result<T, Error> {
serde_json::from_value(value)
}
pub fn to_value<T: Serialize + ?Sized>(value: &T) -> Result<Value, Error> {
serde_json::to_value(value)
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Point {
x: i32,
y: i32,
}
const POINT: Point = Point { x: 1, y: 2 };
#[test]
fn roundtrips_through_string() -> Result<(), Error> {
let text = to_string(&POINT)?;
let restored: Point = from_str(&text)?;
assert_eq!(POINT, restored);
Ok(())
}
#[test]
fn compact_output_has_no_whitespace() -> Result<(), Error> {
let text = to_string(&POINT)?;
assert!(!text.contains('\n'));
assert!(!text.contains(" "));
Ok(())
}
#[test]
fn pretty_output_has_indentation() -> Result<(), Error> {
assert!(
to_string_pretty(&POINT)?.contains('\n'),
"pretty JSON spans lines"
);
Ok(())
}
#[test]
fn from_str_rejects_invalid() {
assert!(
from_str::<Point>("{").is_err(),
"a truncated document is refused"
);
}
#[test]
fn unescaped_string_fields_borrow_from_text_and_slice() -> Result<(), Box<dyn std::error::Error>>
{
crate::serde_facade::json_borrow_contract()?;
Ok(())
}
#[test]
fn escaped_string_cannot_be_returned_as_a_borrowed_str() {
crate::serde_facade::json_escaped_field_is_refused();
}
#[test]
fn borrowed_serializers_accept_str_and_slices() -> Result<(), Error> {
let text: &str = "plain";
assert_eq!(to_string(text)?, "\"plain\"", "str serializes directly");
assert_eq!(
to_string_pretty(text)?,
"\"plain\"",
"pretty serialization accepts str"
);
assert_eq!(
to_vec([1, 2].as_slice())?,
b"[1,2]",
"slices serialize directly"
);
let mut output = Vec::new();
to_writer(&mut output, [1, 2].as_slice())?;
assert_eq!(output, b"[1,2]", "writer serialization accepts slices");
assert_eq!(
to_value([1, 2].as_slice())?,
Value::Array(vec![Value::from(1), Value::from(2)]),
"value-tree serialization accepts slices"
);
Ok(())
}
#[test]
fn parse_errors_keep_utf8_location_and_trailing_content_details() {
let invalid_utf8: Result<Point, _> = from_slice(&[0xFF]);
assert!(
matches!(invalid_utf8, Err(error) if error.classify() == serde_json::error::Category::Syntax),
"invalid UTF-8 is a syntax failure through the facade"
);
let malformed: Result<Point, _> = from_str("{\n \"x\": }");
assert!(
malformed
.as_ref()
.err()
.is_some_and(|error| error.line() == 2 && error.column() > 0),
"syntax errors retain their source line and column"
);
let trailing: Result<Point, _> = from_str(r#"{"x":1,"y":2} trailing"#);
assert!(
matches!(trailing, Err(error) if error.classify() == serde_json::error::Category::Syntax),
"non-whitespace trailing content remains a syntax error"
);
}
}