| use std::borrow::Borrow; | |
| use std::fmt; | |
| use itertools::Itertools as _; | |
| use serde::{Deserialize, Serialize}; | |
| use serde_json::Value; | |
| /// Prerendered json. | |
| /// | |
| /// Both the Display and serde_json::to_string implementations write the serialized json | |
| #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] | |
| #[serde(from = "Value")] | |
| #[serde(into = "Value")] | |
| pub(crate) struct OrderedJson(String); | |
| impl OrderedJson { | |
| /// If you pass in an array, it will not be sorted. | |
| pub(crate) fn serialize<T: Serialize>(item: T) -> Result<Self, serde_json::Error> { | |
| Ok(Self(serde_json::to_string(&item)?)) | |
| } | |
| /// Serializes and sorts | |
| pub(crate) fn array_sorted<T: Borrow<Self>, I: IntoIterator<Item = T>>(items: I) -> Self { | |
| let items = items | |
| .into_iter() | |
| .sorted_unstable_by(|a, b| a.borrow().cmp(b.borrow())) | |
| .format_with(",", |item, f| f(item.borrow())); | |
| Self(format!("[{items}]")) | |
| } | |
| pub(crate) fn array_unsorted<T: Borrow<Self>, I: IntoIterator<Item = T>>(items: I) -> Self { | |
| let items = items.into_iter().format_with(",", |item, f| f(item.borrow())); | |
| Self(format!("[{items}]")) | |
| } | |
| } | |
| impl fmt::Display for OrderedJson { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| self.0.fmt(f) | |
| } | |
| } | |
| impl From<Value> for OrderedJson { | |
| fn from(value: Value) -> Self { | |
| let serialized = | |
| serde_json::to_string(&value).expect("Serializing a Value to String should never fail"); | |
| Self(serialized) | |
| } | |
| } | |
| impl From<OrderedJson> for Value { | |
| fn from(json: OrderedJson) -> Self { | |
| serde_json::from_str(&json.0).expect("OrderedJson should always store valid JSON") | |
| } | |
| } | |
| /// For use in JSON.parse('{...}'). | |
| /// | |
| /// Assumes we are going to be wrapped in single quoted strings. | |
| /// | |
| /// JSON.parse loads faster than raw JS source, | |
| /// so this is used for large objects. | |
| #[derive(Debug, Clone, Serialize, Deserialize)] | |
| pub(crate) struct EscapedJson(OrderedJson); | |
| impl From<OrderedJson> for EscapedJson { | |
| fn from(json: OrderedJson) -> Self { | |
| Self(json) | |
| } | |
| } | |
| impl fmt::Display for EscapedJson { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| // All these `replace` calls are because we have to go through JS string | |
| // for JSON content. | |
| // We need to escape double quotes for the JSON | |
| let json = self.0.0.replace('\\', r"\\").replace('\'', r"\'").replace("\\\"", "\\\\\""); | |
| json.fmt(f) | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests; |