1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3use crate::types::HostEntry;
4
5pub fn sprint_tm(d: Duration) -> String {
6 let ms = d.as_secs_f64() * 1000.0;
7 if ms < 0.0 {
8 format!("{:.2}", ms)
9 } else if ms < 1.0 {
10 format!("{:.3}", ms)
11 } else if ms < 10.0 {
12 format!("{:.2}", ms)
13 } else if ms < 100.0 {
14 format!("{:.1}", ms)
15 } else if ms < 1_000_000.0 {
16 format!("{:.0}", ms)
17 } else {
18 format!("{:.3e}", ms)
19 }
20}
21
22pub fn now_ts() -> String {
23 let secs = SystemTime::now()
24 .duration_since(UNIX_EPOCH)
25 .unwrap_or_default()
26 .as_secs_f64();
27 format!("[{:.5}]", secs)
28}
29
30pub fn max_host_len(hosts: &[HostEntry]) -> usize {
31 hosts.iter().map(|h| h.display.len()).max().unwrap_or(0)
32}
33
34pub fn print_alive(host: &HostEntry, timestamp: bool, json: bool) {
35 let prefix = if timestamp { format!("{} ", now_ts()) } else { String::new() };
36 if json {
37 println!("{{\"alive\": {{\"host\": \"{}\"}}}}", host.display);
38 } else {
39 println!("{}{} is alive", prefix, host.display);
40 }
41}
42
43pub fn print_unreachable(host: &HostEntry, timestamp: bool, json: bool) {
44 let prefix = if timestamp { format!("{} ", now_ts()) } else { String::new() };
45 if json {
46 println!("{{\"unreachable\": {{\"host\": \"{}\"}}}}", host.display);
47 } else {
48 println!("{}{} is unreachable", prefix, host.display);
49 }
50}
51
52pub struct RecvLineOpts<'a> {
53 pub host: &'a HostEntry,
54 pub ping_index: u32,
55 pub rtt: Duration,
56 pub raw_len: usize,
57 pub max_len: usize,
58 pub timestamp: bool,
59 pub json: bool,
60 pub verbose_count: bool,
61}
62
63pub fn print_recv(opts: RecvLineOpts) {
64 let prefix = if opts.timestamp {
65 format!("{} ", now_ts())
66 } else {
67 String::new()
68 };
69
70 let h = opts.host;
71 let rtt_str = sprint_tm(opts.rtt);
72
73 if opts.json {
74 println!(
75 "{{\"resp\": {{\"host\": \"{}\", \"seq\": {}, \"rtt\": {}}}}}",
76 h.display, opts.ping_index, rtt_str
77 );
78 return;
79 }
80
81 if opts.verbose_count {
82 println!(
83 "{}{:<width$} : [{}], {} ms",
84 prefix, h.display, opts.ping_index, rtt_str,
85 width = opts.max_len
86 );
87 return;
88 }
89
90 let avg_str = h.avg_reply().map(sprint_tm).unwrap_or_default();
91 println!(
92 "{}{:<width$} : [{}], {} bytes, {} ms ({} avg, {}% loss)",
93 prefix, h.display, opts.ping_index,
94 opts.raw_len.saturating_sub(28),
95 rtt_str, avg_str, h.loss_pct(),
96 width = opts.max_len
97 );
98}
99
100pub struct TimeoutLineOpts<'a> {
101 pub host: &'a HostEntry,
102 pub ping_index: u32,
103 pub max_len: usize,
104 pub timestamp: bool,
105 pub json: bool,
106}
107
108pub fn print_timeout(opts: TimeoutLineOpts) {
109 let prefix = if opts.timestamp {
110 format!("{} ", now_ts())
111 } else {
112 String::new()
113 };
114
115 let h = opts.host;
116
117 if opts.json {
118 println!(
119 "{{\"timeout\": {{\"host\": \"{}\", \"seq\": {}}}}}",
120 h.display, opts.ping_index
121 );
122 return;
123 }
124
125 print!(
126 "{}{:<width$} : [{}], timed out",
127 prefix, h.display, opts.ping_index,
128 width = opts.max_len
129 );
130
131 match h.avg_reply() {
132 Some(avg) => print!(" ({} avg, ", sprint_tm(avg)),
133 None => print!(" (NaN avg, "),
134 }
135
136 if h.num_recv <= h.num_sent {
137 println!("{}% loss)", h.loss_pct());
138 } else {
139 println!(
140 "{}% return)",
141 (h.num_recv * 100) / h.num_sent.max(1)
142 );
143 }
144}
145
146pub fn print_per_host_stats(host: &HostEntry, max_len: usize, json: bool, verbose: bool) {
147 if verbose {
148 let rtts: Vec<String> = host.resp_times
149 .iter()
150 .map(|r| r.as_ref().map(|d| sprint_tm(*d)).unwrap_or_else(|| "-".into()))
151 .collect();
152
153 if json {
154 let vals: Vec<String> = host.resp_times
155 .iter()
156 .map(|r| r.as_ref().map(|d| sprint_tm(*d)).unwrap_or_else(|| "null".into()))
157 .collect();
158 eprintln!(
159 "{{\"vSum\": {{\"host\": \"{}\", \"values\": [{}]}}}}",
160 host.display,
161 vals.join(", ")
162 );
163 } else {
164 eprint!("{:<width$} :", host.display, width = max_len);
165 for rtt in &rtts {
166 eprint!(" {}", rtt);
167 }
168 eprintln!();
169 }
170 return;
171 }
172
173 if json {
174 eprint!(
175 "{{\"summary\": {{\"host\": \"{}\", \"xmt\": {}, \"rcv\": {}, \"loss\": {}",
176 host.display, host.num_sent, host.num_recv, host.loss_pct()
177 );
178 if let (Some(mn), Some(av), Some(mx)) =
179 (host.min_reply, host.avg_reply(), host.max_reply)
180 {
181 eprint!(
182 ", \"rttMin\": {}, \"rttAvg\": {}, \"rttMax\": {}",
183 sprint_tm(mn), sprint_tm(av), sprint_tm(mx)
184 );
185 }
186 eprintln!("}}}}");
187 } else {
188 eprint!(
189 "{:<width$} : xmt/rcv/%loss = {}/{}/{}%",
190 host.display, host.num_sent, host.num_recv, host.loss_pct(),
191 width = max_len
192 );
193 if let (Some(mn), Some(av), Some(mx)) =
194 (host.min_reply, host.avg_reply(), host.max_reply)
195 {
196 eprint!(
197 ", min/avg/max = {}/{}/{}",
198 sprint_tm(mn), sprint_tm(av), sprint_tm(mx)
199 );
200 }
201 eprintln!();
202 }
203}
204
205pub struct GlobalStatsSummary {
206 pub num_hosts: usize,
207 pub num_alive: usize,
208 pub num_unreachable: usize,
209 pub total_sent: u32,
210 pub total_recv: u32,
211 pub min_rtt: Option<Duration>,
212 pub avg_rtt: Option<Duration>,
213 pub max_rtt: Option<Duration>,
214 pub elapsed: Duration,
215}
216
217pub fn print_global_stats(s: &GlobalStatsSummary, json: bool) {
218 if json {
219 println!(
220 "{{\"stats\": {{\"targets\": {}, \"alive\": {}, \"unreachable\": {}, \
221 \"icmpEchosSent\": {}, \"icmpEchoRepliesReceived\": {}, \"elapsed\": {:.3}}}}}",
222 s.num_hosts, s.num_alive, s.num_unreachable,
223 s.total_sent, s.total_recv,
224 s.elapsed.as_secs_f64()
225 );
226 return;
227 }
228
229 eprintln!();
230 eprintln!(" {:>7} targets", s.num_hosts);
231 eprintln!(" {:>7} alive", s.num_alive);
232 eprintln!(" {:>7} unreachable", s.num_unreachable);
233 eprintln!();
234 eprintln!(" {:>7} ICMP Echos sent", s.total_sent);
235 eprintln!(" {:>7} ICMP Echo Replies received", s.total_recv);
236 eprintln!();
237
238 if let (Some(mn), Some(av), Some(mx)) = (s.min_rtt, s.avg_rtt, s.max_rtt) {
239 eprintln!(" {} ms (min round trip time)", sprint_tm(mn));
240 eprintln!(" {} ms (avg round trip time)", sprint_tm(av));
241 eprintln!(" {} ms (max round trip time)", sprint_tm(mx));
242 }
243
244 eprintln!(" {:>12.3} sec (elapsed real time)", s.elapsed.as_secs_f64());
245 eprintln!();
246}