netspeed_cli/config/source.rs
1//! Raw CLI input types that bridge the CLI layer to the config layer.
2//!
3//! These structs carry unmerged, unvalidated values from [`crate::cli::Args`].
4//! The only place that touches `Args` directly is `ConfigSource::from_args`;
5//! everything downstream depends on these source types, not on the CLI crate.
6
7use super::Format;
8
9/// Raw CLI input values for output settings.
10///
11/// Groups output-related fields from [`crate::cli::Args`] into a cohesive unit
12/// matching the structure of `OutputConfig`.
13///
14/// # Example
15///
16/// ```
17/// use netspeed_cli::config::{Format, OutputSource};
18///
19/// let src = OutputSource {
20/// format: Some(Format::Json),
21/// quiet: Some(true),
22/// ..Default::default()
23/// };
24///
25/// assert_eq!(src.format, Some(Format::Json));
26/// assert_eq!(src.csv_delimiter, ','); // business-logic default preserved
27/// assert_eq!(src.theme, "dark"); // business-logic default preserved
28/// ```
29#[derive(Debug, Clone)]
30pub struct OutputSource {
31 /// Display values in bytes instead of bits
32 pub bytes: Option<bool>,
33 /// Suppress verbose output (deprecated, use format)
34 pub simple: Option<bool>,
35 /// Output in CSV format (deprecated, use format)
36 pub csv: Option<bool>,
37 /// CSV field delimiter
38 pub csv_delimiter: char,
39 /// Include CSV headers
40 pub csv_header: Option<bool>,
41 /// Output in JSON format (deprecated, use format)
42 pub json: Option<bool>,
43 /// Display server list and exit
44 pub list: bool,
45 /// Suppress all progress output
46 pub quiet: Option<bool>,
47 /// Minimal ASCII-only output
48 pub minimal: Option<bool>,
49 /// User profile for customized output
50 pub profile: Option<String>,
51 /// Color theme name
52 pub theme: String,
53 /// Output format (supersedes legacy flags)
54 pub format: Option<Format>,
55}
56
57impl Default for OutputSource {
58 fn default() -> Self {
59 Self {
60 bytes: None,
61 simple: None,
62 csv: None,
63 csv_delimiter: ',',
64 csv_header: None,
65 json: None,
66 list: false,
67 quiet: None,
68 minimal: None,
69 profile: None,
70 theme: "dark".to_string(),
71 format: None,
72 }
73 }
74}
75
76/// Raw CLI input values for test execution settings.
77///
78/// # Example
79///
80/// ```
81/// use netspeed_cli::config::TestSource;
82///
83/// let src = TestSource {
84/// no_download: Some(true),
85/// single: Some(true),
86/// ..Default::default()
87/// };
88///
89/// assert_eq!(src.no_download, Some(true));
90/// assert!(src.no_upload.is_none()); // unset fields default to None
91/// ```
92#[derive(Debug, Clone, Default)]
93pub struct TestSource {
94 /// Do not perform download test
95 pub no_download: Option<bool>,
96 /// Do not perform upload test
97 pub no_upload: Option<bool>,
98 /// Use single connection instead of multiple
99 pub single: Option<bool>,
100}
101
102/// Raw CLI input values for network/transport settings.
103///
104/// # Example
105///
106/// ```
107/// use netspeed_cli::config::NetworkSource;
108///
109/// let src = NetworkSource {
110/// timeout: 30,
111/// tls_version: Some("1.3".to_string()),
112/// ..Default::default()
113/// };
114///
115/// assert_eq!(src.timeout, 30);
116/// assert!(src.source.is_none()); // unset fields default to None
117/// ```
118#[derive(Debug, Clone)]
119pub struct NetworkSource {
120 /// Source IP address to bind to
121 pub source: Option<String>,
122 /// HTTP request timeout in seconds
123 pub timeout: u64,
124 /// Path to custom CA certificate for TLS
125 pub ca_cert: Option<String>,
126 /// Minimum TLS version (1.2 or 1.3)
127 pub tls_version: Option<String>,
128 /// Restrict TLS connections to speedtest.net and ookla.com domains.
129 pub pin_certs: Option<bool>,
130}
131
132impl Default for NetworkSource {
133 fn default() -> Self {
134 Self {
135 source: None,
136 timeout: 10,
137 ca_cert: None,
138 tls_version: None,
139 pin_certs: None,
140 }
141 }
142}
143
144/// Raw CLI input values for server selection settings.
145///
146/// # Example
147///
148/// ```
149/// use netspeed_cli::config::ServerSource;
150///
151/// let src = ServerSource {
152/// server_ids: vec!["1234".to_string()],
153/// ..Default::default()
154/// };
155///
156/// assert_eq!(src.server_ids, vec!["1234"]);
157/// assert!(src.exclude_ids.is_empty()); // unset fields default to empty
158/// ```
159#[derive(Debug, Clone, Default)]
160pub struct ServerSource {
161 /// Specific server IDs to use (empty = auto-select)
162 pub server_ids: Vec<String>,
163 /// Server IDs to exclude from selection
164 pub exclude_ids: Vec<String>,
165}
166
167/// Raw CLI input values extracted from parsed command-line arguments.
168///
169/// The sole bridge between [`crate::cli::Args`] and the config layer.
170/// All downstream config code depends on these source types, not on `Args`.
171///
172/// # Example
173///
174/// ```no_run
175/// use netspeed_cli::config::{
176/// Config, ConfigSource, Format, NetworkSource, OutputSource, TestSource,
177/// };
178///
179/// let source = ConfigSource {
180/// output: OutputSource {
181/// format: Some(Format::Dashboard),
182/// profile: Some("gamer".to_string()),
183/// ..Default::default()
184/// },
185/// test: TestSource {
186/// no_upload: Some(true),
187/// ..Default::default()
188/// },
189/// network: NetworkSource {
190/// timeout: 60,
191/// ..Default::default()
192/// },
193/// ..Default::default()
194/// };
195///
196/// let config = Config::from_source(&source);
197/// assert_eq!(config.timeout(), 60);
198/// assert!(config.no_upload());
199/// ```
200#[derive(Debug, Clone, Default)]
201pub struct ConfigSource {
202 /// Output and display settings
203 pub output: OutputSource,
204 /// Test execution controls
205 pub test: TestSource,
206 /// Network and transport settings
207 pub network: NetworkSource,
208 /// Server selection criteria
209 pub servers: ServerSource,
210 /// Enable strict config validation mode
211 pub strict_config: Option<bool>,
212}
213
214impl ConfigSource {
215 /// Extract config-relevant values from parsed CLI arguments.
216 ///
217 /// This is the **only** method in the config layer that touches
218 /// [`crate::cli::Args`]. All downstream code uses [`ConfigSource`].
219 #[must_use]
220 #[allow(deprecated)] // accesses deprecated --simple/--csv/--json fields for backward compat
221 pub(crate) fn from_args(args: &crate::cli::Args) -> Self {
222 Self {
223 output: OutputSource {
224 bytes: args.bytes,
225 simple: args.simple,
226 csv: args.csv,
227 csv_delimiter: args.csv_delimiter,
228 csv_header: args.csv_header,
229 json: args.json,
230 list: args.list,
231 quiet: args.quiet,
232 minimal: args.minimal,
233 profile: args.profile.clone(),
234 theme: args.theme.clone(),
235 format: args.format.map(Format::from_cli_type),
236 },
237 test: TestSource {
238 no_download: args.no_download,
239 no_upload: args.no_upload,
240 single: args.single,
241 },
242 network: NetworkSource {
243 source: args.source.clone(),
244 timeout: args.timeout,
245 ca_cert: args.ca_cert.clone(),
246 tls_version: args.tls_version.clone(),
247 pin_certs: args.pin_certs,
248 },
249 servers: ServerSource {
250 server_ids: args.server.clone(),
251 exclude_ids: args.exclude.clone(),
252 },
253 strict_config: args.strict_config,
254 }
255 }
256}