prettyping_rs/render/
plain.rs1use crate::app::AppEvent;
2use crate::render::palette::Palette;
3use crate::render::{
4 RenderConfig, format_global_stats_line_plain, format_recent_stats_line, trim_to_width,
5};
6use crate::stats::Stats;
7
8#[derive(Debug, Clone)]
9pub struct PlainRenderer {
10 config: RenderConfig,
11 palette: Palette,
12 stats: Stats,
13 out: String,
14 curr_col: usize,
15}
16
17impl PlainRenderer {
18 #[must_use]
19 pub fn new(config: RenderConfig) -> Self {
20 let palette = Palette::from_flags(
21 config.unicode,
22 config.color,
23 config.multicolor,
24 config.rttmin,
25 config.rttmax,
26 );
27
28 let mut out = String::new();
29 if config.legend {
30 out.push_str(&palette.legend_line_painted());
31 out.push('\n');
32 }
33
34 Self {
35 stats: Stats::new(config.last),
36 config,
37 palette,
38 out,
39 curr_col: 0,
40 }
41 }
42
43 pub fn render_event(&mut self, event: &AppEvent) {
44 self.stats.apply(event);
45
46 let Some(symbol) = self.event_symbol(event) else {
47 return;
48 };
49
50 self.out.push_str(&symbol);
51 self.curr_col = self.curr_col.saturating_add(1);
52
53 let wrap_at = usize::try_from(self.config.last).unwrap_or(usize::MAX);
54 if wrap_at > 0 && self.curr_col >= wrap_at {
55 self.out.push('\n');
56 self.curr_col = 0;
57 self.append_stats_block();
58 }
59 }
60
61 pub fn finish(&mut self) {
62 if self.curr_col > 0 {
63 self.out.push('\n');
64 self.curr_col = 0;
65 }
66 self.append_stats_block();
67 }
68
69 #[must_use]
70 pub fn output(&self) -> &str {
71 &self.out
72 }
73
74 pub fn output_mut(&mut self) -> &mut String {
75 &mut self.out
76 }
77
78 #[must_use]
79 pub fn into_output(self) -> String {
80 self.out
81 }
82
83 fn append_stats_block(&mut self) {
84 if self.config.globalstats {
85 let line = format_global_stats_line_plain(&self.stats.global_snapshot());
86 self.out.push_str(&self.trim(line));
87 self.out.push('\n');
88 }
89
90 if self.config.recentstats && self.config.last > 0 {
91 let line = format_recent_stats_line(&self.stats.recent_snapshot());
92 self.out.push_str(&self.trim(line));
93 self.out.push('\n');
94 }
95 }
96
97 fn event_symbol(&self, event: &AppEvent) -> Option<String> {
98 match event {
99 AppEvent::ProbeReply { rtt_ms, .. } => {
100 let item = self
101 .palette
102 .item_for_rtt(u32::try_from(*rtt_ms).unwrap_or(u32::MAX));
103 Some(self.palette.paint(item))
104 }
105 AppEvent::ProbeTimeout { .. } => {
106 if self.config.color {
107 Some("\x1b[0;31m!\x1b[0m".to_owned())
108 } else {
109 Some("!".to_owned())
110 }
111 }
112 _ => None,
113 }
114 }
115
116 fn trim(&self, line: String) -> String {
117 self.config
118 .columns
119 .map(|width| trim_to_width(&line, usize::from(width)))
120 .unwrap_or(line)
121 }
122}