rperf3/
config.rs

1use serde::{Deserialize, Serialize};
2use std::net::IpAddr;
3use std::time::Duration;
4
5/// Transport protocol type for network testing.
6///
7/// Specifies whether to use TCP or UDP for the performance test.
8///
9/// # Examples
10///
11/// ```
12/// use rperf3::{Config, Protocol};
13/// use std::time::Duration;
14///
15/// // TCP test
16/// let tcp_config = Config::client("127.0.0.1".to_string(), 5201)
17///     .with_protocol(Protocol::Tcp);
18///
19/// // UDP test with bandwidth limit
20/// let udp_config = Config::client("127.0.0.1".to_string(), 5201)
21///     .with_protocol(Protocol::Udp)
22///     .with_bandwidth(100_000_000); // 100 Mbps
23/// ```
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25pub enum Protocol {
26    /// Transmission Control Protocol - provides reliable, ordered delivery
27    Tcp,
28    /// User Datagram Protocol - provides best-effort delivery with lower overhead
29    Udp,
30}
31
32impl Protocol {
33    /// Returns the protocol name as a static string.
34    ///
35    /// This avoids memory allocation when converting protocol to string,
36    /// providing a performance optimization over `format!("{:?}", protocol)`.
37    ///
38    /// # Returns
39    ///
40    /// A static string slice containing the protocol name.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use rperf3::Protocol;
46    ///
47    /// assert_eq!(Protocol::Tcp.as_str(), "Tcp");
48    /// assert_eq!(Protocol::Udp.as_str(), "Udp");
49    /// ```
50    pub const fn as_str(self) -> &'static str {
51        match self {
52            Protocol::Tcp => "Tcp",
53            Protocol::Udp => "Udp",
54        }
55    }
56}
57
58/// Test mode: client or server.
59///
60/// Determines whether this instance acts as a server (listening for connections)
61/// or as a client (initiating connections to a server).
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63pub enum Mode {
64    /// Server mode - listens for incoming connections
65    Server,
66    /// Client mode - connects to a server and initiates tests
67    Client,
68}
69
70/// Configuration for rperf3 network performance tests.
71///
72/// This structure holds all configuration parameters for both client and server modes.
73/// Use the builder pattern methods to customize the configuration.
74///
75/// # Examples
76///
77/// ## Basic TCP Client
78///
79/// ```
80/// use rperf3::Config;
81/// use std::time::Duration;
82///
83/// let config = Config::client("192.168.1.100".to_string(), 5201)
84///     .with_duration(Duration::from_secs(30))
85///     .with_buffer_size(256 * 1024); // 256 KB buffer
86/// ```
87///
88/// ## UDP Client with Bandwidth Limit
89///
90/// ```
91/// use rperf3::{Config, Protocol};
92/// use std::time::Duration;
93///
94/// let config = Config::client("192.168.1.100".to_string(), 5201)
95///     .with_protocol(Protocol::Udp)
96///     .with_bandwidth(100_000_000) // 100 Mbps
97///     .with_duration(Duration::from_secs(10));
98/// ```
99///
100/// ## Server Configuration
101///
102/// ```
103/// use rperf3::Config;
104///
105/// let config = Config::server(5201);
106/// ```
107///
108/// ## Reverse Mode Test
109///
110/// ```
111/// use rperf3::Config;
112/// use std::time::Duration;
113///
114/// // Server sends data, client receives
115/// let config = Config::client("192.168.1.100".to_string(), 5201)
116///     .with_reverse(true)
117///     .with_duration(Duration::from_secs(10));
118/// ```
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct Config {
121    /// Server mode or client mode
122    pub mode: Mode,
123
124    /// Protocol to use (TCP or UDP)
125    pub protocol: Protocol,
126
127    /// Port number to use
128    pub port: u16,
129
130    /// Server address (for client mode)
131    pub server_addr: Option<String>,
132
133    /// Bind address (for server mode)
134    pub bind_addr: Option<IpAddr>,
135
136    /// Test duration in seconds
137    pub duration: Duration,
138
139    /// Target bandwidth in bits per second (for UDP)
140    pub bandwidth: Option<u64>,
141
142    /// Buffer size in bytes
143    pub buffer_size: usize,
144
145    /// Number of parallel streams
146    pub parallel: usize,
147
148    /// Reverse mode (server sends, client receives)
149    pub reverse: bool,
150
151    /// Output in JSON format
152    pub json: bool,
153
154    /// Interval for periodic bandwidth reports in seconds
155    pub interval: Duration,
156}
157
158impl Default for Config {
159    fn default() -> Self {
160        Self {
161            mode: Mode::Client,
162            protocol: Protocol::Tcp,
163            port: 5201,
164            server_addr: None,
165            bind_addr: None,
166            duration: Duration::from_secs(10),
167            bandwidth: None,
168            buffer_size: 128 * 1024, // 128 KB
169            parallel: 1,
170            reverse: false,
171            json: false,
172            interval: Duration::from_secs(1),
173        }
174    }
175}
176
177impl Config {
178    /// Creates a new configuration with default values.
179    ///
180    /// This is equivalent to calling `Config::default()`. The default configuration
181    /// is set up for client mode with TCP protocol.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use rperf3::Config;
187    ///
188    /// let config = Config::new();
189    /// assert_eq!(config.port, 5201);
190    /// ```
191    pub fn new() -> Self {
192        Self::default()
193    }
194
195    /// Creates a new server configuration.
196    ///
197    /// Sets up the configuration for server mode, which listens for incoming
198    /// connections on the specified port.
199    ///
200    /// # Arguments
201    ///
202    /// * `port` - The port number to listen on (typically 5201)
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use rperf3::Config;
208    ///
209    /// let config = Config::server(5201);
210    /// ```
211    pub fn server(port: u16) -> Self {
212        Self {
213            mode: Mode::Server,
214            port,
215            ..Default::default()
216        }
217    }
218
219    /// Creates a new client configuration.
220    ///
221    /// Sets up the configuration for client mode, which connects to a server
222    /// at the specified address and port.
223    ///
224    /// # Arguments
225    ///
226    /// * `server_addr` - The IP address or hostname of the server
227    /// * `port` - The port number to connect to (typically 5201)
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// use rperf3::Config;
233    ///
234    /// let config = Config::client("192.168.1.100".to_string(), 5201);
235    /// ```
236    pub fn client(server_addr: String, port: u16) -> Self {
237        Self {
238            mode: Mode::Client,
239            server_addr: Some(server_addr),
240            port,
241            ..Default::default()
242        }
243    }
244
245    /// Sets the protocol to use for the test.
246    ///
247    /// # Arguments
248    ///
249    /// * `protocol` - Either `Protocol::Tcp` or `Protocol::Udp`
250    ///
251    /// # Examples
252    ///
253    /// ```
254    /// use rperf3::{Config, Protocol};
255    ///
256    /// let config = Config::client("127.0.0.1".to_string(), 5201)
257    ///     .with_protocol(Protocol::Udp);
258    /// ```
259    pub fn with_protocol(mut self, protocol: Protocol) -> Self {
260        self.protocol = protocol;
261        self
262    }
263
264    /// Sets the test duration.
265    ///
266    /// # Arguments
267    ///
268    /// * `duration` - How long the test should run
269    ///
270    /// # Examples
271    ///
272    /// ```
273    /// use rperf3::Config;
274    /// use std::time::Duration;
275    ///
276    /// let config = Config::client("127.0.0.1".to_string(), 5201)
277    ///     .with_duration(Duration::from_secs(30));
278    /// ```
279    pub fn with_duration(mut self, duration: Duration) -> Self {
280        self.duration = duration;
281        self
282    }
283
284    /// Sets the target bandwidth for tests.
285    ///
286    /// Controls the send rate for both TCP and UDP tests. The bandwidth limiter
287    /// uses a rate-based algorithm that checks bandwidth every 1ms and sleeps
288    /// when sending too fast.
289    ///
290    /// # Arguments
291    ///
292    /// * `bandwidth` - Target bandwidth in bits per second
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// use rperf3::{Config, Protocol};
298    ///
299    /// // UDP test at 100 Mbps
300    /// let udp_config = Config::client("127.0.0.1".to_string(), 5201)
301    ///     .with_protocol(Protocol::Udp)
302    ///     .with_bandwidth(100_000_000); // 100 Mbps
303    ///
304    /// // TCP test at 50 Mbps
305    /// let tcp_config = Config::client("127.0.0.1".to_string(), 5201)
306    ///     .with_protocol(Protocol::Tcp)
307    ///     .with_bandwidth(50_000_000); // 50 Mbps
308    /// ```
309    pub fn with_bandwidth(mut self, bandwidth: u64) -> Self {
310        self.bandwidth = Some(bandwidth);
311        self
312    }
313
314    /// Sets the buffer size for data transfer.
315    ///
316    /// Larger buffer sizes can improve throughput but use more memory.
317    ///
318    /// # Arguments
319    ///
320    /// * `size` - Buffer size in bytes (default: 128 KB)
321    ///
322    /// # Examples
323    ///
324    /// ```
325    /// use rperf3::Config;
326    ///
327    /// let config = Config::client("127.0.0.1".to_string(), 5201)
328    ///     .with_buffer_size(256 * 1024); // 256 KB
329    /// ```
330    pub fn with_buffer_size(mut self, size: usize) -> Self {
331        self.buffer_size = size;
332        self
333    }
334
335    /// Sets the number of parallel streams.
336    ///
337    /// Multiple parallel streams can improve throughput by utilizing multiple
338    /// connections simultaneously. This is particularly useful for high-bandwidth
339    /// networks where a single stream might not saturate the link.
340    ///
341    /// # Arguments
342    ///
343    /// * `parallel` - Number of parallel streams (default: 1)
344    ///
345    /// # Examples
346    ///
347    /// ```
348    /// use rperf3::Config;
349    ///
350    /// let config = Config::client("127.0.0.1".to_string(), 5201)
351    ///     .with_parallel(4); // Use 4 parallel streams
352    /// ```
353    pub fn with_parallel(mut self, parallel: usize) -> Self {
354        self.parallel = parallel;
355        self
356    }
357
358    /// Enables or disables reverse mode.
359    ///
360    /// In reverse mode, the server sends data and the client receives.
361    /// In normal mode, the client sends data and the server receives.
362    ///
363    /// # Arguments
364    ///
365    /// * `reverse` - `true` for reverse mode, `false` for normal mode
366    ///
367    /// # Examples
368    ///
369    /// ```
370    /// use rperf3::Config;
371    ///
372    /// let config = Config::client("127.0.0.1".to_string(), 5201)
373    ///     .with_reverse(true);
374    /// ```
375    pub fn with_reverse(mut self, reverse: bool) -> Self {
376        self.reverse = reverse;
377        self
378    }
379
380    /// Enables or disables JSON output format.
381    ///
382    /// When enabled, results are output in machine-readable JSON format
383    /// similar to iperf3.
384    ///
385    /// # Arguments
386    ///
387    /// * `json` - `true` to enable JSON output, `false` for human-readable
388    ///
389    /// # Examples
390    ///
391    /// ```
392    /// use rperf3::Config;
393    ///
394    /// let config = Config::client("127.0.0.1".to_string(), 5201)
395    ///     .with_json(true);
396    /// ```
397    pub fn with_json(mut self, json: bool) -> Self {
398        self.json = json;
399        self
400    }
401
402    /// Sets the interval for periodic reporting.
403    ///
404    /// Statistics will be reported at this interval during the test.
405    ///
406    /// # Arguments
407    ///
408    /// * `interval` - How often to report statistics (default: 1 second)
409    ///
410    /// # Examples
411    ///
412    /// ```
413    /// use rperf3::Config;
414    /// use std::time::Duration;
415    ///
416    /// let config = Config::client("127.0.0.1".to_string(), 5201)
417    ///     .with_interval(Duration::from_secs(2));
418    /// ```
419    pub fn with_interval(mut self, interval: Duration) -> Self {
420        self.interval = interval;
421        self
422    }
423}