api_testing_core/
markdown.rs1use crate::Result;
2
3fn sort_json(value: &serde_json::Value) -> serde_json::Value {
4 match value {
5 serde_json::Value::Object(map) => {
6 let mut keys: Vec<&String> = map.keys().collect();
7 keys.sort();
8 let mut out = serde_json::Map::new();
9 for k in keys {
10 let v = map.get(k).expect("key exists");
11 out.insert(k.clone(), sort_json(v));
12 }
13 serde_json::Value::Object(out)
14 }
15 serde_json::Value::Array(values) => {
16 serde_json::Value::Array(values.iter().map(sort_json).collect())
17 }
18 other => other.clone(),
19 }
20}
21
22pub fn format_json_pretty_sorted(value: &serde_json::Value) -> Result<String> {
24 let sorted = sort_json(value);
25 Ok(serde_json::to_string_pretty(&sorted)?)
26}
27
28pub fn heading(level: u8, text: &str) -> String {
29 let level = level.clamp(1, 6);
30 format!("{} {}\n", "#".repeat(level.into()), text.trim())
31}
32
33pub fn code_block(lang: &str, body: &str) -> String {
34 let mut out = String::new();
35 out.push_str("```");
36 out.push_str(lang.trim());
37 out.push('\n');
38 out.push_str(body);
39 if !body.ends_with('\n') {
40 out.push('\n');
41 }
42 out.push_str("```\n");
43 out
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use pretty_assertions::assert_eq;
50
51 #[test]
52 fn markdown_code_block_is_newline_stable() {
53 assert_eq!(code_block("json", "{ }"), "```json\n{ }\n```\n");
54 assert_eq!(code_block("json", "{ }\n"), "```json\n{ }\n```\n");
55 }
56
57 #[test]
58 fn markdown_heading_trims_and_clamps_level() {
59 assert_eq!(heading(1, " Title "), "# Title\n");
60 assert_eq!(heading(9, "Title"), "###### Title\n");
61 }
62
63 #[test]
64 fn json_format_sorts_keys_recursively() {
65 let v = serde_json::json!({"b": 1, "a": {"d": 4, "c": 3}});
66 let s = format_json_pretty_sorted(&v).unwrap();
67 assert_eq!(
68 s,
69 "{\n \"a\": {\n \"c\": 3,\n \"d\": 4\n },\n \"b\": 1\n}"
70 );
71 }
72}