@@ -26,6 +26,14 @@ struct Args {
26
26
/// Output mode: normal, json, or csv
27
27
#[ arg( short = 'o' , long, value_enum, default_value_t = OutputMode :: Normal ) ]
28
28
output_mode : OutputMode ,
29
+
30
+ /// Exit immediately after a successful probe
31
+ #[ arg( short = 'e' , long) ]
32
+ exit_on_success : bool ,
33
+
34
+ /// Calculate and display jitter
35
+ #[ arg( short = 'j' , long) ]
36
+ jitter : bool ,
29
37
}
30
38
31
39
#[ derive( ValueEnum , Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -39,6 +47,7 @@ enum OutputMode {
39
47
struct PingResult {
40
48
success : bool ,
41
49
duration_ms : f64 ,
50
+ jitter_ms : Option < f64 > ,
42
51
addr : std:: net:: SocketAddr ,
43
52
}
44
53
@@ -84,7 +93,7 @@ fn main() {
84
93
ctrlc:: set_handler ( move || {
85
94
r. store ( false , Ordering :: SeqCst ) ;
86
95
} )
87
- . expect ( "Error setting Ctrl-C handler" ) ;
96
+ . expect ( "Error setting Ctrl-C handler" ) ;
88
97
89
98
if args. continuous && args. output_mode == OutputMode :: Normal {
90
99
println ! ( ) ;
@@ -113,35 +122,67 @@ fn main() {
113
122
total_duration += duration;
114
123
successful_pings += 1 ;
115
124
125
+ let avg_duration = total_duration / successful_pings as f64 ;
126
+ let jitter = if args. jitter {
127
+ Some ( ( duration - avg_duration) . abs ( ) )
128
+ } else {
129
+ None
130
+ } ;
131
+
116
132
let result = PingResult {
117
133
success : true ,
118
134
duration_ms : duration,
135
+ jitter_ms : jitter,
119
136
addr,
120
137
} ;
121
138
results. push ( result. clone ( ) ) ;
122
139
123
140
match args. output_mode {
124
141
OutputMode :: Normal => {
125
- println ! (
126
- "Probing {}/tcp - Port is open - time={:.4}ms" ,
127
- addr, duration
128
- ) ;
142
+ if args. jitter {
143
+ println ! (
144
+ "Probing {}/tcp - Port is open - time={:.4}ms jitter={:.4}ms" ,
145
+ addr,
146
+ duration,
147
+ jitter. unwrap_or( 0.0 )
148
+ ) ;
149
+ } else {
150
+ println ! (
151
+ "Probing {}/tcp - Port is open - time={:.4}ms" ,
152
+ addr, duration
153
+ ) ;
154
+ }
129
155
}
130
156
OutputMode :: Json => {
131
157
let json = serde_json:: to_string ( & result) . unwrap ( ) ;
132
158
println ! ( "{}" , json) ;
133
159
}
134
160
OutputMode :: Csv => {
135
- println ! ( "{},{},{:.4}" , addr, "open" , duration) ;
161
+ if args. jitter {
162
+ println ! (
163
+ "{},{},{:.4},{:.4}" ,
164
+ addr,
165
+ "open" ,
166
+ duration,
167
+ jitter. unwrap_or( 0.0 )
168
+ ) ;
169
+ } else {
170
+ println ! ( "{},{},{:.4}" , addr, "open" , duration) ;
171
+ }
136
172
}
137
173
}
174
+
175
+ if args. exit_on_success {
176
+ break ;
177
+ }
138
178
}
139
179
Err ( _) => {
140
180
let duration = timeout. as_micros ( ) as f64 / 1000.0 ;
141
181
142
182
let result = PingResult {
143
183
success : false ,
144
184
duration_ms : duration,
185
+ jitter_ms : None ,
145
186
addr,
146
187
} ;
147
188
results. push ( result. clone ( ) ) ;
@@ -164,67 +205,92 @@ fn main() {
164
205
}
165
206
}
166
207
208
+ if !args. continuous && total_attempts >= args. count {
209
+ break ;
210
+ }
211
+
167
212
thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
168
213
}
169
214
170
- let avg_duration = if successful_pings > 0 {
171
- total_duration / successful_pings as f64
172
- } else {
173
- 0.0
174
- } ;
175
-
176
- let packet_loss = 100.0 * ( 1.0 - ( successful_pings as f64 / total_attempts as f64 ) ) ;
177
-
178
- let summary = Summary {
179
- addr,
180
- total_attempts,
181
- successful_pings,
182
- packet_loss,
183
- min_duration_ms : if successful_pings > 0 {
184
- min_duration
185
- } else {
186
- 0.0
187
- } ,
188
- avg_duration_ms : avg_duration,
189
- max_duration_ms : if successful_pings > 0 {
190
- max_duration
215
+ if args. output_mode == OutputMode :: Normal {
216
+ let avg_duration = if successful_pings > 0 {
217
+ total_duration / successful_pings as f64
191
218
} else {
192
219
0.0
193
- } ,
194
- } ;
220
+ } ;
195
221
196
- match args. output_mode {
197
- OutputMode :: Normal => {
198
- println ! ( "\n --- {} tcping statistics ---" , addr) ;
199
- println ! (
200
- "{} probes sent, {} successful, {:.2}% packet loss" ,
201
- total_attempts, successful_pings, packet_loss
202
- ) ;
203
- if successful_pings > 0 {
204
- println ! (
205
- "Round-trip min/avg/max = {:.4}ms/{:.4}ms/{:.4}ms" ,
206
- min_duration, avg_duration, max_duration
207
- ) ;
208
- }
209
- println ! ( ) ;
210
- }
211
- OutputMode :: Json => {
212
- let json = serde_json:: to_string ( & summary) . unwrap ( ) ;
213
- println ! ( "{}" , json) ;
214
- }
215
- OutputMode :: Csv => {
216
- println ! ( "address,total_probes,successful_probes,packet_loss,min_rtt,avg_rtt,max_rtt" ) ;
222
+ let packet_loss = 100.0 * ( 1.0 - ( successful_pings as f64 / total_attempts as f64 ) ) ;
223
+
224
+ let summary = Summary {
225
+ addr,
226
+ total_attempts,
227
+ successful_pings,
228
+ packet_loss,
229
+ min_duration_ms : if successful_pings > 0 {
230
+ min_duration
231
+ } else {
232
+ 0.0
233
+ } ,
234
+ avg_duration_ms : avg_duration,
235
+ max_duration_ms : if successful_pings > 0 {
236
+ max_duration
237
+ } else {
238
+ 0.0
239
+ } ,
240
+ } ;
241
+
242
+ println ! ( "\n --- {} tcping statistics ---" , addr) ;
243
+ println ! (
244
+ "{} probes sent, {} successful, {:.2}% packet loss" ,
245
+ summary. total_attempts, summary. successful_pings, summary. packet_loss
246
+ ) ;
247
+ if summary. successful_pings > 0 {
217
248
println ! (
218
- "{},{},{},{:.2},{:.4},{:.4},{:.4}" ,
219
- addr,
220
- total_attempts,
221
- successful_pings,
222
- packet_loss,
223
- summary. min_duration_ms,
224
- summary. avg_duration_ms,
225
- summary. max_duration_ms
249
+ "Round-trip min/avg/max = {:.4}ms/{:.4}ms/{:.4}ms" ,
250
+ summary. min_duration_ms, summary. avg_duration_ms, summary. max_duration_ms
226
251
) ;
227
252
}
253
+ println ! ( ) ;
254
+ } else if args. output_mode == OutputMode :: Json {
255
+ let summary = Summary {
256
+ addr,
257
+ total_attempts,
258
+ successful_pings,
259
+ packet_loss : 100.0 * ( 1.0 - ( successful_pings as f64 / total_attempts as f64 ) ) ,
260
+ min_duration_ms : if successful_pings > 0 {
261
+ min_duration
262
+ } else {
263
+ 0.0
264
+ } ,
265
+ avg_duration_ms : if successful_pings > 0 {
266
+ total_duration / successful_pings as f64
267
+ } else {
268
+ 0.0
269
+ } ,
270
+ max_duration_ms : if successful_pings > 0 {
271
+ max_duration
272
+ } else {
273
+ 0.0
274
+ } ,
275
+ } ;
276
+ let json = serde_json:: to_string ( & summary) . unwrap ( ) ;
277
+ println ! ( "{}" , json) ;
278
+ } else if args. output_mode == OutputMode :: Csv {
279
+ println ! ( "address,total_probes,successful_probes,packet_loss,min_rtt,avg_rtt,max_rtt" ) ;
280
+ println ! (
281
+ "{},{},{},{:.2},{:.4},{:.4},{:.4}" ,
282
+ addr,
283
+ total_attempts,
284
+ successful_pings,
285
+ 100.0 * ( 1.0 - ( successful_pings as f64 / total_attempts as f64 ) ) ,
286
+ if successful_pings > 0 { min_duration } else { 0.0 } ,
287
+ if successful_pings > 0 {
288
+ total_duration / successful_pings as f64
289
+ } else {
290
+ 0.0
291
+ } ,
292
+ if successful_pings > 0 { max_duration } else { 0.0 }
293
+ ) ;
228
294
}
229
295
}
230
296
@@ -240,6 +306,8 @@ mod tests {
240
306
assert_eq ! ( args. count, 5 ) ;
241
307
assert ! ( !args. continuous) ;
242
308
assert_eq ! ( args. output_mode, OutputMode :: Normal ) ;
309
+ assert ! ( !args. exit_on_success) ;
310
+ assert ! ( !args. jitter) ;
243
311
}
244
312
245
313
#[ test]
@@ -249,6 +317,8 @@ mod tests {
249
317
assert_eq ! ( args. count, 4 ) ;
250
318
assert ! ( args. continuous) ;
251
319
assert_eq ! ( args. output_mode, OutputMode :: Normal ) ;
320
+ assert ! ( !args. exit_on_success) ;
321
+ assert ! ( !args. jitter) ;
252
322
}
253
323
254
324
#[ test]
@@ -263,4 +333,16 @@ mod tests {
263
333
let args = Args :: parse_from ( "tcping 127.0.0.1:80 -o json" . split_whitespace ( ) ) ;
264
334
assert_eq ! ( args. output_mode, OutputMode :: Json ) ;
265
335
}
336
+
337
+ #[ test]
338
+ fn test_exit_on_success_parsing ( ) {
339
+ let args = Args :: parse_from ( "tcping 127.0.0.1:80 -e" . split_whitespace ( ) ) ;
340
+ assert ! ( args. exit_on_success) ;
341
+ }
342
+
343
+ #[ test]
344
+ fn test_jitter_parsing ( ) {
345
+ let args = Args :: parse_from ( "tcping 127.0.0.1:80 -j" . split_whitespace ( ) ) ;
346
+ assert ! ( args. jitter) ;
347
+ }
266
348
}
0 commit comments