-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #128252 - EtomicBomb:pre-rfc, r=<try>
modularize rustdoc's write_shared Refactor src/librustdoc/html/render/write_shared.rs to reduce code duplication, adding unit tests * Extract + unit test code for sorting and rendering JSON, which is duplicated 9 times in the current impl * Extract + unit test code for encoding JSON as single quoted strings, which is duplicated twice in the current impl * Unit tests for cross-crate information file formats * Generic interface to add new kinds of cross-crate information files in the future * Intended to match current behavior exactly, except for a merge info comment it adds to the bottom of cci files * This PR is intended to reduce the review burden from my [mergeable rustdoc rfc](rust-lang/rfcs#3662) implementation PR, which is a [small commit based on this branch](https://github.com/EtomicBomb/rust/tree/rfc). This code is agnostic to the RFC and does not include any of the flags discussed there, but cleanly enables the addition of these flags in the future because it is more modular
- Loading branch information
Showing
9 changed files
with
1,555 additions
and
683 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use std::borrow::Borrow; | ||
use std::fmt; | ||
|
||
use itertools::Itertools as _; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_json::Value; | ||
|
||
/// Prerenedered 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(OrderedJson(serde_json::to_string(&item)?)) | ||
} | ||
|
||
/// Serializes and sorts | ||
pub(crate) fn array_sorted<T: Borrow<OrderedJson>, 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())); | ||
OrderedJson(format!("[{}]", items)) | ||
} | ||
|
||
pub(crate) fn array_unsorted<T: Borrow<OrderedJson>, I: IntoIterator<Item = T>>( | ||
items: I, | ||
) -> Self { | ||
let items = items.into_iter().format_with(",", |item, f| f(item.borrow())); | ||
OrderedJson(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"); | ||
OrderedJson(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 { | ||
EscapedJson(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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use super::super::ordered_json::*; | ||
|
||
fn check(json: OrderedJson, serialized: &str) { | ||
assert_eq!(json.to_string(), serialized); | ||
assert_eq!(serde_json::to_string(&json).unwrap(), serialized); | ||
|
||
let json = json.to_string(); | ||
let json: OrderedJson = serde_json::from_str(&json).unwrap(); | ||
|
||
assert_eq!(json.to_string(), serialized); | ||
assert_eq!(serde_json::to_string(&json).unwrap(), serialized); | ||
|
||
let json = serde_json::to_string(&json).unwrap(); | ||
let json: OrderedJson = serde_json::from_str(&json).unwrap(); | ||
|
||
assert_eq!(json.to_string(), serialized); | ||
assert_eq!(serde_json::to_string(&json).unwrap(), serialized); | ||
} | ||
|
||
// Make sure there is no extra level of string, plus number of escapes. | ||
#[test] | ||
fn escape_json_number() { | ||
let json = OrderedJson::serialize(3).unwrap(); | ||
let json = EscapedJson::from(json); | ||
assert_eq!(format!("{json}"), "3"); | ||
} | ||
|
||
#[test] | ||
fn escape_json_single_quote() { | ||
let json = OrderedJson::serialize("he's").unwrap(); | ||
let json = EscapedJson::from(json); | ||
assert_eq!(format!("{json}"), r#""he\'s""#); | ||
} | ||
|
||
#[test] | ||
fn escape_json_array() { | ||
let json = OrderedJson::serialize([1, 2, 3]).unwrap(); | ||
let json = EscapedJson::from(json); | ||
assert_eq!(format!("{json}"), r#"[1,2,3]"#); | ||
} | ||
|
||
#[test] | ||
fn escape_json_string() { | ||
let json = OrderedJson::serialize(r#"he"llo"#).unwrap(); | ||
let json = EscapedJson::from(json); | ||
assert_eq!(format!("{json}"), r#""he\\\"llo""#); | ||
} | ||
|
||
#[test] | ||
fn escape_json_string_escaped() { | ||
let json = OrderedJson::serialize(r#"he\"llo"#).unwrap(); | ||
let json = EscapedJson::from(json); | ||
assert_eq!(format!("{json}"), r#""he\\\\\\\"llo""#); | ||
} | ||
|
||
#[test] | ||
fn escape_json_string_escaped_escaped() { | ||
let json = OrderedJson::serialize(r#"he\\"llo"#).unwrap(); | ||
let json = EscapedJson::from(json); | ||
assert_eq!(format!("{json}"), r#""he\\\\\\\\\\\"llo""#); | ||
} | ||
|
||
// Testing round trip + making sure there is no extra level of string | ||
#[test] | ||
fn number() { | ||
let json = OrderedJson::serialize(3).unwrap(); | ||
let serialized = "3"; | ||
check(json, serialized); | ||
} | ||
|
||
#[test] | ||
fn boolean() { | ||
let json = OrderedJson::serialize(true).unwrap(); | ||
let serialized = "true"; | ||
check(json, serialized); | ||
} | ||
|
||
#[test] | ||
fn string() { | ||
let json = OrderedJson::serialize("he\"llo").unwrap(); | ||
let serialized = r#""he\"llo""#; | ||
check(json, serialized); | ||
} | ||
|
||
#[test] | ||
fn serialize_array() { | ||
let json = OrderedJson::serialize([3, 1, 2]).unwrap(); | ||
let serialized = "[3,1,2]"; | ||
check(json, serialized); | ||
} | ||
|
||
#[test] | ||
fn sorted_array() { | ||
let items = ["c", "a", "b"]; | ||
let serialized = r#"["a","b","c"]"#; | ||
let items: Vec<OrderedJson> = | ||
items.into_iter().map(OrderedJson::serialize).collect::<Result<Vec<_>, _>>().unwrap(); | ||
let json = OrderedJson::array_sorted(items); | ||
check(json, serialized); | ||
} | ||
|
||
#[test] | ||
fn nested_array() { | ||
let a = OrderedJson::serialize(3).unwrap(); | ||
let b = OrderedJson::serialize(2).unwrap(); | ||
let c = OrderedJson::serialize(1).unwrap(); | ||
let d = OrderedJson::serialize([1, 3, 2]).unwrap(); | ||
let json = OrderedJson::array_sorted([a, b, c, d]); | ||
let serialized = r#"[1,2,3,[1,3,2]]"#; | ||
check(json, serialized); | ||
} | ||
|
||
#[test] | ||
fn array_unsorted() { | ||
let items = ["c", "a", "b"]; | ||
let serialized = r#"["c","a","b"]"#; | ||
let items: Vec<OrderedJson> = | ||
items.into_iter().map(OrderedJson::serialize).collect::<Result<Vec<_>, _>>().unwrap(); | ||
let json = OrderedJson::array_unsorted(items); | ||
check(json, serialized); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.