1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
extern crate alloc;

use alloc::{format, string::String};

use serde::Serialize;
use serde_json::{json, Value};

const MAX_STRING_LEN: usize = 100;

/// Serialize the given data structure as a pretty-printed String of JSON using
/// `serde_json::to_string_pretty()`, but after first reducing any string values over
/// `MAX_STRING_LEN` to display the field's number of chars instead of the actual value.
pub fn json_pretty_print<T>(value: &T) -> serde_json::Result<String>
where
    T: ?Sized + Serialize,
{
    let mut json_value = json!(value);
    shorten_string_field(&mut json_value);

    serde_json::to_string_pretty(&json_value)
}

fn shorten_string_field(value: &mut Value) {
    match value {
        Value::String(string) => {
            if string.len() > MAX_STRING_LEN {
                *string = format!("{} chars", string.len());
            }
        }
        Value::Array(values) => {
            for value in values {
                shorten_string_field(value);
            }
        }
        Value::Object(map) => {
            for map_value in map.values_mut() {
                shorten_string_field(map_value);
            }
        }
        Value::Null | Value::Bool(_) | Value::Number(_) => {}
    }
}

#[cfg(test)]
mod tests {
    use core::iter::{self, FromIterator};

    use super::*;

    #[test]
    fn should_shorten_long_strings() {
        let mut long_strings = vec![];
        for i in 1..=5 {
            long_strings.push(String::from_iter(
                iter::repeat('a').take(MAX_STRING_LEN + i),
            ));
        }
        let value = json!({
            "field_1": Option::<usize>::None,
            "field_2": true,
            "field_3": 123,
            "field_4": long_strings[0],
            "field_5": [
                "short string value",
                long_strings[1]
            ],
            "field_6": {
                "f1": Option::<usize>::None,
                "f2": false,
                "f3": -123,
                "f4": long_strings[2],
                "f5": [
                    "short string value",
                    long_strings[3]
                ],
                "f6": {
                    "final long string": long_strings[4]
                }
            }
        });

        let expected = r#"{
  "field_1": null,
  "field_2": true,
  "field_3": 123,
  "field_4": "101 chars",
  "field_5": [
    "short string value",
    "102 chars"
  ],
  "field_6": {
    "f1": null,
    "f2": false,
    "f3": -123,
    "f4": "103 chars",
    "f5": [
      "short string value",
      "104 chars"
    ],
    "f6": {
      "final long string": "105 chars"
    }
  }
}"#;

        let output = json_pretty_print(&value).unwrap();
        assert_eq!(output, expected);
    }

    #[test]
    fn should_not_modify_short_strings() {
        let max_string = String::from_iter(iter::repeat('a').take(MAX_STRING_LEN));
        let value = json!({
            "field_1": Option::<usize>::None,
            "field_2": true,
            "field_3": 123,
            "field_4": max_string,
            "field_5": [
                "short string value",
                "another short string"
            ],
            "field_6": {
                "f1": Option::<usize>::None,
                "f2": false,
                "f3": -123,
                "f4": "short",
                "f5": [
                    "short string value",
                    "another short string"
                ],
                "f6": {
                    "final string": "the last short string"
                }
            }
        });

        let expected = serde_json::to_string_pretty(&value).unwrap();
        let output = json_pretty_print(&value).unwrap();
        assert_eq!(output, expected);
    }
}