Skip to main content

prettyping_rs/render/
plain.rs

1use 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}