prettier_print/
prettier_printer.rs1use rand::distributions::{Bernoulli, Distribution};
2use rand::rngs::SmallRng;
3use rand::{Rng, SeedableRng};
4use rand_distr::WeightedAliasIndex;
5use std::fmt::{Debug, Display, Formatter};
6use std::iter::repeat;
7
8pub type Seed = <SmallRng as SeedableRng>::Seed;
9
10#[derive(Debug, Clone)]
12pub struct PrettierPrinter {
13 rng: SmallRng,
14}
15
16impl PrettierPrinter {
17 pub fn new_with_seed(seed: Seed) -> Self {
19 Self {
20 rng: SmallRng::from_seed(seed),
21 }
22 }
23
24 pub fn gen_seed(rng: &mut SmallRng) -> Seed {
26 let mut seed = Seed::default();
27 rng.fill(&mut seed);
28 seed
29 }
30
31 pub fn print<'a, T>(&mut self, inner: &'a T) -> PrettierPrintDisplayer<'a, T> {
33 PrettierPrintDisplayer {
34 seed: PrettierPrinter::gen_seed(&mut self.rng),
35 inner,
36 }
37 }
38}
39
40impl Default for PrettierPrinter {
41 fn default() -> Self {
43 Self {
44 rng: SmallRng::from_entropy(),
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
52pub struct PrettierPrintDisplayer<'a, T> {
53 seed: Seed,
54 inner: &'a T,
55}
56
57impl<T> PrettierPrintDisplayer<'_, T> {
58 pub fn output(seed: Seed, debug_str: &str) -> String {
59 const RAINBOW: char = '🌈';
60 const STARS: &[char] = &['⭐', '🌟', '☀', '🦀'];
61 let weights: Vec<u16> = vec![1500, 300, 100, 1];
62
63 let mut rng = SmallRng::from_seed(seed);
64 let mut line_rng = Bernoulli::from_ratio(3, 5)
65 .unwrap() .sample_iter(SmallRng::from_seed(PrettierPrinter::gen_seed(&mut rng)));
67
68 let mut star_rng = WeightedAliasIndex::new(weights.to_vec())
69 .unwrap()
70 .sample_iter(SmallRng::from_seed(PrettierPrinter::gen_seed(&mut rng)));
71
72 let width = debug_str
73 .lines()
74 .map(|s| s.len())
75 .max()
76 .map_or(0, |n| n + n / 10 + 2);
77
78 let mut result = RAINBOW.to_string();
79 result.extend(repeat(' ').take(width - 2));
80 result.push(RAINBOW);
81 result.push('\n');
82
83 for line in debug_str.lines() {
84 result.push(' ');
85
86 let leading_space_count = line.bytes().take_while(|&b| b == b' ').count();
87
88 if leading_space_count > 0 && line_rng.next().unwrap() {
90 let star_index = rng.gen_range(0..leading_space_count);
92 result.extend(repeat(' ').take(star_index));
93
94 result.push(STARS[star_rng.next().unwrap()]);
95 result.extend(repeat(' ').take(leading_space_count - star_index - 1));
96
97 result += line.split_at(leading_space_count).1;
98 } else {
99 result.push_str(line);
101 }
102
103 if line_rng.next().unwrap() {
105 let star_index = rng.gen_range(0..width - line.len());
106 result.extend(repeat(' ').take(star_index));
107 result.push(STARS[star_rng.next().unwrap()]);
108 }
109
110 while result.ends_with(' ') {
112 result.pop();
113 }
114
115 result.push('\n');
116 }
117
118 result.push(RAINBOW);
119 result.extend(repeat(' ').take(width - 2));
120 result.push(RAINBOW);
121 result.push('\n');
122 result
123 }
124}
125
126impl<T> Display for PrettierPrintDisplayer<'_, T>
127where
128 T: Debug,
129{
130 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
131 write!(
132 f,
133 "{}",
134 PrettierPrintDisplayer::<T>::output(self.seed, &format!("{:#?}", self.inner))
135 )
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use std::collections::HashMap;
143
144 #[test]
145 fn prettier_printer() {
146 let seed = {
147 let mut seed = Seed::default();
148 seed[0] = 180; seed
150 };
151 {
152 let result = PrettierPrinter::new_with_seed(seed).print(&0).to_string();
153 assert!(result.starts_with("🌈 🌈\n"));
154 assert!(result.ends_with("🌈 🌈\n"));
155 assert!(result.contains(' '));
156 }
157 {
158 #[derive(Debug, Clone)]
159 struct Type {
160 a: String,
161 b: Vec<i32>,
162 c: HashMap<&'static str, &'static str>,
163 }
164
165 let input = Type {
166 a: "a".to_string(),
167 b: vec![0, 1],
168 c: {
169 let mut map = HashMap::new();
170 map.insert("So", "pretty");
171 map
172 },
173 };
174
175 let displayer = PrettierPrinter::new_with_seed(seed).print(&input);
176
177 let result = displayer.to_string();
178 assert!(result.starts_with("🌈 🌈\n"));
179 assert!(result.ends_with("🌈 🌈\n"));
180 assert_eq!(result, displayer.clone().to_string());
182
183 println!("\n{:#?}\n\n", &input);
184 println!("{}\n", result);
185 }
186 }
187}