1use std::fmt; 2 3use serde::Serialize; 4use serde_json::value::RawValue; 5 6use crate::ProtocolError; 7 8/// A JSON value kept as the exact bytes that will be sent. The API 9/// accepts a string, an object or an array wherever it takes free-form 10/// content (`state`, `instructions`, descriptions). 11#[derive(Clone)] 12pub struct Json(Box<RawValue>); 13 14impl Json { 15 /// A JSON string holding `text`. 16 pub fn text(text: &str) -> Self { 17 Json(RawValue::from_string(serde_json::to_string(text).expect("a str serializes")).expect("valid JSON")) 18 } 19 20 /// `json` as written, after checking it is JSON. 21 pub fn verbatim(json: &str) -> Result<Self, ProtocolError> { 22 RawValue::from_string(json.to_owned()) 23 .map(Json) 24 .map_err(|e| ProtocolError::Invalid(format!("not JSON: {e}"))) 25 } 26 27 /// `json` in RFC 8785 canonical form: sorted keys, no whitespace, 28 /// ECMAScript number formatting, and numbers a double cannot hold 29 /// exactly kept as strings. Equal values give equal bytes. 30 pub fn canonical(json: &str) -> Result<Self, ProtocolError> { 31 let canonical = crate::jcs::canonicalize(json).map_err(|e| ProtocolError::Invalid(format!("not JSON: {e}")))?; 32 Json::verbatim(&canonical) 33 } 34 35 pub fn as_str(&self) -> &str { 36 self.0.get() 37 } 38} 39 40impl fmt::Debug for Json { 41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 42 f.write_str(self.as_str()) 43 } 44} 45 46impl PartialEq for Json { 47 fn eq(&self, other: &Self) -> bool { 48 self.as_str() == other.as_str() 49 } 50} 51 52impl Serialize for Json { 53 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { 54 self.0.serialize(s) 55 } 56} 57 58#[cfg(test)] 59mod tests { 60 use super::*; 61 62 #[test] 63 fn canonical_sorts_keys_and_formats_numbers() { 64 let j = Json::canonical(r#"{ "id": 1, "body": "hi", "a": 1.0e0, "big": 1e21, "bigint": 9007199254740993 }"#).unwrap(); 65 assert_eq!(j.as_str(), r#"{"a":1,"big":1e+21,"bigint":"9007199254740993","body":"hi","id":1}"#); 66 } 67 68 #[test] 69 fn text_is_a_json_string() { 70 assert_eq!(Json::text("say \"hi\"").as_str(), r#""say \"hi\"""#); 71 } 72 73 #[test] 74 fn verbatim_keeps_bytes() { 75 assert_eq!(Json::verbatim(r#"{"b":1, "a":2}"#).unwrap().as_str(), r#"{"b":1, "a":2}"#); 76 assert!(Json::verbatim("{").is_err()); 77 } 78}