goose/
config.rs

1//! Functions and structures related to configuring a Goose load test.
2//!
3//! Goose can be configured at run time by passing in the options and flags defined by
4//! the [`GooseConfiguration`] structure.
5//!
6//! Goose can be configured programmatically with [`GooseDefaultType::set_default`].
7
8use gumdrop::Options;
9use serde::{Deserialize, Serialize};
10use simplelog::*;
11use std::path::PathBuf;
12use std::str::FromStr;
13
14use crate::logger::GooseLogFormat;
15use crate::metrics::GooseCoordinatedOmissionMitigation;
16use crate::test_plan::TestPlan;
17use crate::util;
18use crate::{GooseAttack, GooseError};
19
20/// Runtime options available when launching a Goose load test.
21///
22/// Custom defaults can be programmatically set for most of these options using the
23/// `GooseDefaults` structure.
24///
25/// [Help is generated for all of these options](https://book.goose.rs/getting-started/runtime-options.html)
26/// by passing a `-h` flag to an application built with the Goose Library.
27///
28/// Goose leverages [`gumdrop`](https://docs.rs/gumdrop/) to derive the above help from
29/// the the below structure.
30#[derive(Options, Debug, Clone, Default, Serialize, Deserialize)]
31#[options(
32    help = r#"Goose is a modern, high-performance, distributed HTTP(S) load testing tool,
33written in Rust. Visit https://book.goose.rs/ for more information.
34
35The following runtime options are available when launching a Goose load test:"#
36)]
37pub struct GooseConfiguration {
38    /// Displays this help
39    #[options(short = "h")]
40    pub help: bool,
41    /// Prints version information
42    #[options(short = "V")]
43    pub version: bool,
44    /// Lists all transactions and exits
45    // Add a blank line after this option
46    #[options(short = "l", help = "Lists all transactions and exits\n")]
47    pub list: bool,
48
49    /// Defines host to load test (ie http://10.21.32.33)
50    #[options(short = "H")]
51    pub host: String,
52    /// Sets concurrent users (default: number of CPUs)
53    #[options(short = "u")]
54    pub users: Option<usize>,
55    /// Sets per-second user hatch rate (default: 1)
56    #[options(short = "r", meta = "RATE")]
57    pub hatch_rate: Option<String>,
58    /// Starts users for up to (30s, 20m, 3h, 1h30m, etc)
59    #[options(short = "s", meta = "TIME")]
60    pub startup_time: String,
61    /// Stops load test after (30s, 20m, 3h, 1h30m, etc)
62    #[options(short = "t", meta = "TIME")]
63    pub run_time: String,
64    /// Enables Goose log file and sets name
65    #[options(short = "G", meta = "NAME")]
66    pub goose_log: String,
67    /// Increases Goose log level (-g, -gg, etc)
68    #[options(short = "g", count)]
69    pub log_level: u8,
70    /// Decreases Goose verbosity (-q, -qq, etc)
71    #[options(count, short = "q", help = "Decreases Goose verbosity (-q, -qq, etc)")]
72    pub quiet: u8,
73    /// Increases Goose verbosity (-v, -vv, etc)
74    #[options(
75        count,
76        short = "v",
77        // Add a blank line and then a 'Metrics:' header after this option
78        help = "Increases Goose verbosity (-v, -vv, etc)\n\nMetrics:"
79    )]
80    pub verbose: u8,
81
82    /// How often to optionally print running metrics
83    #[options(no_short, meta = "TIME")]
84    pub running_metrics: Option<usize>,
85    /// Doesn't reset metrics after all users have started
86    #[options(no_short)]
87    pub no_reset_metrics: bool,
88    /// Doesn't track metrics
89    #[options(no_short)]
90    pub no_metrics: bool,
91    /// Doesn't track transaction metrics
92    #[options(no_short)]
93    pub no_transaction_metrics: bool,
94    /// Doesn't track scenario metrics
95    #[options(no_short)]
96    pub no_scenario_metrics: bool,
97    /// Doesn't display metrics at end of load test
98    #[options(no_short)]
99    pub no_print_metrics: bool,
100    /// Doesn't display an error summary
101    #[options(no_short)]
102    pub no_error_summary: bool,
103    /// Create reports, can be used multiple times (supports .html, .htm, .md, .json)
104    #[options(no_short, meta = "NAME")]
105    pub report_file: Vec<String>,
106    /// Disable granular graphs in report file
107    #[options(no_short)]
108    pub no_granular_report: bool,
109    /// Sets request log file name
110    #[options(short = "R", meta = "NAME")]
111    pub request_log: String,
112    /// Sets request log format (csv, json, raw, pretty)
113    #[options(no_short, meta = "FORMAT")]
114    pub request_format: Option<GooseLogFormat>,
115    /// Include the request body in the request log
116    #[options(no_short)]
117    pub request_body: bool,
118    /// Sets transaction log file name
119    #[options(short = "T", meta = "NAME")]
120    pub transaction_log: String,
121    /// Sets log format (csv, json, raw, pretty)
122    #[options(no_short, meta = "FORMAT")]
123    pub transaction_format: Option<GooseLogFormat>,
124    /// Sets scenario log file name
125    #[options(short = "S", meta = "NAME")]
126    pub scenario_log: String,
127    /// Sets log format (csv, json, raw, pretty)
128    #[options(no_short, meta = "FORMAT")]
129    pub scenario_format: Option<GooseLogFormat>,
130    /// Sets error log file name
131    #[options(short = "E", meta = "NAME")]
132    pub error_log: String,
133    /// Sets error log format (csv, json, raw, pretty)
134    #[options(no_short, meta = "FORMAT")]
135    pub error_format: Option<GooseLogFormat>,
136    /// Sets debug log file name
137    #[options(short = "D", meta = "NAME")]
138    pub debug_log: String,
139    /// Sets debug log format (csv, json, raw, pretty)
140    #[options(no_short, meta = "FORMAT")]
141    pub debug_format: Option<GooseLogFormat>,
142    /// Do not include the response body in the debug log
143    #[options(no_short)]
144    pub no_debug_body: bool,
145    /// Do not track status code metrics
146    // Add a blank line and then an Advanced: header after this option
147    #[options(no_short, help = "Do not track status code metrics\n\nAdvanced:")]
148    pub no_status_codes: bool,
149
150    /// Defines a more complex test plan ("10,60s;0,30s")
151    #[options(no_short, meta = "\"TESTPLAN\"")]
152    pub(crate) test_plan: Option<TestPlan>,
153    /// Sets how many times to run scenarios then exit
154    #[options(no_short)]
155    pub iterations: usize,
156    /// Limits load test to only specified scenarios
157    #[options(no_short, meta = "\"SCENARIO\"")]
158    pub scenarios: Scenarios,
159    /// Lists all scenarios and exits
160    #[options(no_short)]
161    pub scenarios_list: bool,
162    /// Doesn't enable telnet Controller
163    #[options(no_short)]
164    pub no_telnet: bool,
165    /// Sets telnet Controller host (default: 0.0.0.0)
166    #[options(no_short, meta = "HOST")]
167    pub telnet_host: String,
168    /// Sets telnet Controller TCP port (default: 5116)
169    #[options(no_short, meta = "PORT")]
170    pub telnet_port: u16,
171    /// Doesn't enable WebSocket Controller
172    #[options(no_short)]
173    pub no_websocket: bool,
174    /// Sets WebSocket Controller host (default: 0.0.0.0)
175    #[options(no_short, meta = "HOST")]
176    pub websocket_host: String,
177    /// Sets WebSocket Controller TCP port (default: 5117)
178    #[options(no_short, meta = "PORT")]
179    pub websocket_port: u16,
180    /// Doesn't automatically start load test
181    #[options(no_short)]
182    pub no_autostart: bool,
183    /// Doesn't set the gzip Accept-Encoding header
184    #[options(no_short)]
185    pub no_gzip: bool,
186    /// Sets per-request timeout, in seconds (default: 60)
187    #[options(no_short, meta = "VALUE")]
188    pub timeout: Option<String>,
189    /// Sets coordinated omission mitigation strategy
190    #[options(no_short, meta = "STRATEGY")]
191    pub co_mitigation: Option<GooseCoordinatedOmissionMitigation>,
192    /// Sets maximum requests per second
193    #[options(no_short, meta = "VALUE")]
194    pub throttle_requests: usize,
195    /// Follows base_url redirect with subsequent requests
196    #[options(no_short)]
197    pub sticky_follow: bool,
198    /// Disables validation of https certificates
199    #[options(no_short)]
200    pub accept_invalid_certs: bool,
201}
202
203/// Optionally defines a subset of active Scenarios to run during a load test.
204#[derive(Options, Default, Debug, Clone, Serialize, Deserialize)]
205pub struct Scenarios {
206    pub active: Vec<String>,
207}
208/// Implement [`FromStr`] to convert `"foo,bar"` comma separated string to a vector of strings.
209impl FromStr for Scenarios {
210    type Err = GooseError;
211
212    fn from_str(s: &str) -> Result<Self, Self::Err> {
213        // Convert string into a vector of string.
214        let mut active: Vec<String> = Vec::new();
215        // Multiple Scenarios can be defined as a comma separated list.
216        let lines = s.split(',');
217        for line in lines {
218            // Ignore white space an case.
219            let scenario = line.trim().to_lowercase();
220            // Valid scenario names are alphanumeric only.
221            if scenario.chars().all(char::is_alphanumeric) {
222                active.push(scenario);
223            } else {
224                // Logger isn't initialized yet, provide helpful debug output.
225                eprintln!("ERROR: invalid `configuration.scenarios` value: '{}'", line);
226                eprintln!("  Expected format: --scenarios \"{{one}},{{two}},{{three}}\"");
227                eprintln!("    {{one}}, {{two}}, {{three}}, etc must be alphanumeric");
228                eprintln!("    To view valid scenario names invoke `--scenarios-list`");
229                return Err(GooseError::InvalidOption {
230                    option: "`configuration.scenarios".to_string(),
231                    value: line.to_string(),
232                    detail: "invalid `configuration.scenarios` value.".to_string(),
233                });
234            }
235        }
236        // The listed scenarios are only valid if the logic gets this far.
237        Ok(Scenarios { active })
238    }
239}
240
241/// Optional default values for Goose run-time options.
242///
243/// These custom defaults can be configured using [`GooseDefaultType::set_default()`].
244#[derive(Clone, Debug, Default)]
245pub(crate) struct GooseDefaults {
246    /// An optional default host to run this load test against.
247    pub host: Option<String>,
248    /// An optional default number of users to simulate.
249    pub users: Option<usize>,
250    /// An optional default number of clients to start per second.
251    pub hatch_rate: Option<String>,
252    /// An optional default number of seconds for the test to start.
253    pub startup_time: Option<usize>,
254    /// An optional default number of seconds for the test to run.
255    pub run_time: Option<usize>,
256    /// An optional default test plan.
257    pub test_plan: Option<TestPlan>,
258    /// An optional default test plan.
259    pub iterations: Option<usize>,
260    /// Optional default scenarios.
261    pub scenarios: Option<Scenarios>,
262    /// An optional default log level.
263    pub log_level: Option<u8>,
264    /// An optional default for the goose log file name.
265    pub goose_log: Option<String>,
266    /// An optional default value for quiet level.
267    pub quiet: Option<u8>,
268    /// An optional default value for verbosity level.
269    pub verbose: Option<u8>,
270    /// An optional default for printing running metrics.
271    pub running_metrics: Option<usize>,
272    /// An optional default for not resetting metrics after all users started.
273    pub no_reset_metrics: Option<bool>,
274    /// An optional default for not tracking metrics.
275    pub no_metrics: Option<bool>,
276    /// An optional default for not tracking transaction metrics.
277    pub no_transaction_metrics: Option<bool>,
278    /// An optional default for not tracking scenario metrics.
279    pub no_scenario_metrics: Option<bool>,
280    /// An optional default for not displaying metrics at the end of the load test.
281    pub no_print_metrics: Option<bool>,
282    /// An optional default for not displaying an error summary.
283    pub no_error_summary: Option<bool>,
284    /// An optional default for the html-formatted report file name.
285    pub report_file: Option<Vec<String>>,
286    /// An optional default for the flag that disables granular data in HTML report graphs.
287    pub no_granular_report: Option<bool>,
288    /// An optional default for the requests log file name.
289    pub request_log: Option<String>,
290    /// An optional default for the requests log file format.
291    pub request_format: Option<GooseLogFormat>,
292    /// An optional default for logging the request body.
293    pub request_body: Option<bool>,
294    /// An optional default for the transaction log file name.
295    pub transaction_log: Option<String>,
296    /// An optional default for the transaction log file format.
297    pub transaction_format: Option<GooseLogFormat>,
298    /// An optional default for the scenario log file name.
299    pub scenario_log: Option<String>,
300    /// An optional default for the scenario log file format.
301    pub scenario_format: Option<GooseLogFormat>,
302    /// An optional default for the error log file name.
303    pub error_log: Option<String>,
304    /// An optional default for the error log format.
305    pub error_format: Option<GooseLogFormat>,
306    /// An optional default for the debug log file name.
307    pub debug_log: Option<String>,
308    /// An optional default for the debug log format.
309    pub debug_format: Option<GooseLogFormat>,
310    /// An optional default for not logging response body in debug log.
311    pub no_debug_body: Option<bool>,
312    /// An optional default for not enabling telnet Controller thread.
313    pub no_telnet: Option<bool>,
314    /// An optional default for not enabling WebSocket Controller thread.
315    pub no_websocket: Option<bool>,
316    /// An optional default for not auto-starting the load test.
317    pub no_autostart: Option<bool>,
318    /// An optional default for not setting the gzip Accept-Encoding header.
319    pub no_gzip: Option<bool>,
320    /// An optional default number of seconds to timeout requests.
321    pub timeout: Option<String>,
322    /// An optional default for coordinated omission mitigation.
323    pub co_mitigation: Option<GooseCoordinatedOmissionMitigation>,
324    /// An optional default to not track status code metrics.
325    pub no_status_codes: Option<bool>,
326    /// An optional default maximum requests per second.
327    pub throttle_requests: Option<usize>,
328    /// An optional default to follows base_url redirect with subsequent request.
329    pub sticky_follow: Option<bool>,
330    /// An optional default for host telnet Controller listens on.
331    pub telnet_host: Option<String>,
332    /// An optional default for port telnet Controller listens on.
333    pub telnet_port: Option<u16>,
334    /// An optional default for host WebSocket Controller listens on.
335    pub websocket_host: Option<String>,
336    /// An optional default for port WebSocket Controller listens on.
337    pub websocket_port: Option<u16>,
338    /// An optional default for not validating https certificates.
339    pub accept_invalid_certs: Option<bool>,
340}
341
342/// Defines all [`GooseConfiguration`] options that can be programmatically configured with
343/// a custom default.
344///
345/// These custom defaults can be configured using [`GooseDefaultType::set_default()`].
346#[derive(Debug)]
347pub enum GooseDefault {
348    /// An optional default host to run this load test against.
349    Host,
350    /// An optional default number of users to simulate.
351    Users,
352    /// An optional default number of clients to start per second.
353    HatchRate,
354    /// An optional default number of seconds for the test to start up.
355    StartupTime,
356    /// An optional default number of seconds for the test to run.
357    RunTime,
358    /// An optional default test plan.
359    TestPlan,
360    /// An optional default number of iterations to run scenarios then exit.
361    Iterations,
362    /// Optional default list of scenarios to run.
363    Scenarios,
364    /// An optional default log level.
365    LogLevel,
366    /// An optional default for the log file name.
367    GooseLog,
368    /// An optional default value for quiet level.
369    Quiet,
370    /// An optional default value for verbosity level.
371    Verbose,
372    /// An optional default for printing running metrics.
373    RunningMetrics,
374    /// An optional default for not resetting metrics after all users started.
375    NoResetMetrics,
376    /// An optional default for not tracking metrics.
377    NoMetrics,
378    /// An optional default for not tracking transaction metrics.
379    NoTransactionMetrics,
380    /// An optional default for not tracking scneario metrics.
381    NoScenarioMetrics,
382    /// An optional default for not displaying metrics at end of load test.
383    NoPrintMetrics,
384    /// An optional default for not displaying an error summary.
385    NoErrorSummary,
386    /// An optional default for the report file name.
387    ReportFile,
388    /// An optional default for the flag that disables granular data in HTML report graphs.
389    NoGranularData,
390    /// An optional default for the request log file name.
391    RequestLog,
392    /// An optional default for the request log file format.
393    RequestFormat,
394    /// An optional default for logging the request body.
395    RequestBody,
396    /// An optional default for the transaction log file name.
397    TransactionLog,
398    /// An optional default for the transaction log file format.
399    TransactionFormat,
400    /// An optional default for the scenario log file name.
401    ScenarioLog,
402    /// An optional default for the scenario log file format.
403    ScenarioFormat,
404    /// An optional default for the error log file name.
405    ErrorLog,
406    /// An optional default for the error log format.
407    ErrorFormat,
408    /// An optional default for the debug log file name.
409    DebugLog,
410    /// An optional default for the debug log format.
411    DebugFormat,
412    /// An optional default for not logging the response body in the debug log.
413    NoDebugBody,
414    /// An optional default for not enabling telnet Controller thread.
415    NoTelnet,
416    /// An optional default for not enabling WebSocket Controller thread.
417    NoWebSocket,
418    /// An optional default for coordinated omission mitigation.
419    CoordinatedOmissionMitigation,
420    /// An optional default for not automatically starting load test.
421    NoAutoStart,
422    /// An optional default timeout for all requests, in seconds.
423    Timeout,
424    /// An optional default for not setting the gzip Accept-Encoding header.
425    NoGzip,
426    /// An optional default to not track status code metrics.
427    NoStatusCodes,
428    /// An optional default maximum requests per second.
429    ThrottleRequests,
430    /// An optional default to follows base_url redirect with subsequent request.
431    StickyFollow,
432    /// An optional default for host telnet Controller listens on.
433    TelnetHost,
434    /// An optional default for port telnet Controller listens on.
435    TelnetPort,
436    /// An optional default for host Websocket Controller listens on.
437    WebSocketHost,
438    /// An optional default for port WebSocket Controller listens on.
439    WebSocketPort,
440    /// An optional default for not validating https certificates.
441    AcceptInvalidCerts,
442}
443
444/// Most run-time options can be programmatically configured with custom defaults.
445///
446/// For example, you can optionally configure a default host for the load test. This is
447/// used if no per-[`Scenario`](../struct.Scenario.html) host is defined, no
448/// [`--host`](./enum.GooseDefault.html#variant.Host) CLI option is configured, and if
449/// the [`Transaction`](../struct.Transaction.html) itself doesn't hard-code the host in
450/// the base url of its request. In that case, this host is added to all requests.
451///
452/// In the following example, the load test is programmatically configured with
453/// [`GooseDefaultType::set_default`] to default to running against a local development
454/// container. The [`--host`](./enum.GooseDefault.html#variant.Host) run time option
455/// can be used at start time to override the host value, and the
456/// [`GooseControllerCommand::host`](../controller/enum.GooseControllerCommand.html#variant.Host)
457/// Controller command can be used to change the host value of an
458/// [`AttackPhase::idle`](../enum.AttackPhase.html#variant.Idle) load test.
459///
460/// # Example
461/// ```rust
462/// use goose::prelude::*;
463///
464/// #[tokio::main]
465/// async fn main() -> Result<(), GooseError> {
466///     GooseAttack::initialize()?
467///         .set_default(GooseDefault::Host, "local.dev")?;
468///
469///     Ok(())
470/// }
471/// ```
472///
473/// The following run-time options can be configured with a custom default using a
474/// borrowed string slice ([`&str`]):
475///  - [`GooseDefault::DebugLog`]
476///  - [`GooseDefault::ErrorLog`]
477///  - [`GooseDefault::GooseLog`]
478///  - [`GooseDefault::HatchRate`]
479///  - [`GooseDefault::Host`]
480///  - [`GooseDefault::ReportFile`]
481///  - [`GooseDefault::RequestLog`]
482///  - [`GooseDefault::ScenarioLog`]
483///  - [`GooseDefault::Scenarios`]
484///  - [`GooseDefault::TelnetHost`]
485///  - [`GooseDefault::TestPlan`]
486///  - [`GooseDefault::Timeout`]
487///  - [`GooseDefault::TransactionLog`]
488///  - [`GooseDefault::WebSocketHost`]
489///
490/// The following run-time options can be configured with a custom default using a
491/// [`usize`] integer:
492///  - [`GooseDefault::Users`]
493///  - [`GooseDefault::StartupTime`]
494///  - [`GooseDefault::RunTime`]
495///  - [`GooseDefault::Iterations`]
496///  - [`GooseDefault::RunningMetrics`]
497///  - [`GooseDefault::LogLevel`]
498///  - [`GooseDefault::Quiet`]
499///  - [`GooseDefault::Verbose`]
500///  - [`GooseDefault::ThrottleRequests`]
501///  - [`GooseDefault::TelnetPort`]
502///  - [`GooseDefault::WebSocketPort`]
503///
504/// The following run-time flags can be configured with a custom default using a
505/// [`bool`] (and otherwise default to [`false`]).
506///  - [`GooseDefault::NoResetMetrics`]
507///  - [`GooseDefault::NoPrintMetrics`]
508///  - [`GooseDefault::NoMetrics`]
509///  - [`GooseDefault::NoTransactionMetrics`]
510///  - [`GooseDefault::NoScenarioMetrics`]
511///  - [`GooseDefault::RequestBody`]
512///  - [`GooseDefault::NoErrorSummary`]
513///  - [`GooseDefault::NoDebugBody`]
514///  - [`GooseDefault::NoTelnet`]
515///  - [`GooseDefault::NoWebSocket`]
516///  - [`GooseDefault::NoAutoStart`]
517///  - [`GooseDefault::NoGzip`]
518///  - [`GooseDefault::NoStatusCodes`]
519///  - [`GooseDefault::StickyFollow`]
520///  - [`GooseDefault::NoGranularData`]
521///
522/// The following run-time flags can be configured with a custom default using a
523/// [`GooseLogFormat`].
524///  - [`GooseDefault::RequestFormat`]
525///  - [`GooseDefault::TransactionFormat`]
526///  - [`GooseDefault::ScenarioFormat`]
527///  - [`GooseDefault::ErrorFormat`]
528///  - [`GooseDefault::DebugFormat`]
529///
530/// The following run-time flags can be configured with a custom default using a
531/// [`GooseCoordinatedOmissionMitigation`].
532///  - [`GooseDefault::CoordinatedOmissionMitigation`]
533pub trait GooseDefaultType<T> {
534    /// Sets a [`GooseDefault`] to the provided value. The required type of each option
535    /// is documented in [`GooseDefaultType`].
536    ///
537    /// # Example
538    /// ```rust
539    /// use goose::prelude::*;
540    ///
541    /// #[tokio::main]
542    /// async fn main() -> Result<(), GooseError> {
543    ///     GooseAttack::initialize()?
544    ///         // Do not reset the metrics after the load test finishes starting.
545    ///         .set_default(GooseDefault::NoResetMetrics, true)?
546    ///         // Do not display info level logs while the test runs.
547    ///         .set_default(GooseDefault::Quiet, 1)?
548    ///         // Log all requests made during the test to `./goose-request.log`.
549    ///         .set_default(GooseDefault::RequestLog, "goose-request.log")?;
550    ///
551    ///     Ok(())
552    /// }
553    /// ```
554    fn set_default(self, key: GooseDefault, value: T) -> Result<Box<Self>, GooseError>;
555}
556impl GooseDefaultType<&str> for GooseAttack {
557    /// Sets [`GooseDefault`] to a [`&str`] value.
558    fn set_default(mut self, key: GooseDefault, value: &str) -> Result<Box<Self>, GooseError> {
559        match key {
560            // Set valid defaults.
561            GooseDefault::DebugLog => self.defaults.debug_log = Some(value.to_string()),
562            GooseDefault::ErrorLog => self.defaults.error_log = Some(value.to_string()),
563            GooseDefault::GooseLog => self.defaults.goose_log = Some(value.to_string()),
564            GooseDefault::HatchRate => self.defaults.hatch_rate = Some(value.to_string()),
565            GooseDefault::Host => {
566                self.defaults.host = if value.is_empty() {
567                    None
568                } else {
569                    Some(value.to_string())
570                }
571            }
572            GooseDefault::ReportFile => self.defaults.report_file = Some(vec![value.to_string()]),
573            GooseDefault::RequestLog => self.defaults.request_log = Some(value.to_string()),
574            GooseDefault::ScenarioLog => self.defaults.scenario_log = Some(value.to_string()),
575            GooseDefault::Scenarios => {
576                self.defaults.scenarios = Some(value.parse::<Scenarios>().unwrap())
577            }
578            GooseDefault::TelnetHost => self.defaults.telnet_host = Some(value.to_string()),
579            GooseDefault::TestPlan => {
580                self.defaults.test_plan = Some(value.parse::<TestPlan>().unwrap())
581            }
582            GooseDefault::Timeout => self.defaults.timeout = Some(value.to_string()),
583            GooseDefault::TransactionLog => self.defaults.transaction_log = Some(value.to_string()),
584            GooseDefault::WebSocketHost => self.defaults.websocket_host = Some(value.to_string()),
585            // Otherwise display a helpful and explicit error.
586            GooseDefault::Users
587            | GooseDefault::StartupTime
588            | GooseDefault::RunTime
589            | GooseDefault::Iterations
590            | GooseDefault::LogLevel
591            | GooseDefault::Quiet
592            | GooseDefault::Verbose
593            | GooseDefault::ThrottleRequests
594            | GooseDefault::TelnetPort
595            | GooseDefault::WebSocketPort => {
596                return Err(GooseError::InvalidOption {
597                    option: format!("GooseDefault::{:?}", key),
598                    value: value.to_string(),
599                    detail: format!(
600                        "set_default(GooseDefault::{:?}, {}) expected usize value, received &str",
601                        key, value
602                    ),
603                });
604            }
605            GooseDefault::RunningMetrics
606            | GooseDefault::NoResetMetrics
607            | GooseDefault::NoMetrics
608            | GooseDefault::NoTransactionMetrics
609            | GooseDefault::NoScenarioMetrics
610            | GooseDefault::RequestBody
611            | GooseDefault::NoPrintMetrics
612            | GooseDefault::NoErrorSummary
613            | GooseDefault::NoDebugBody
614            | GooseDefault::NoTelnet
615            | GooseDefault::NoWebSocket
616            | GooseDefault::NoAutoStart
617            | GooseDefault::NoGzip
618            | GooseDefault::NoStatusCodes
619            | GooseDefault::StickyFollow
620            | GooseDefault::NoGranularData
621            | GooseDefault::AcceptInvalidCerts => {
622                return Err(GooseError::InvalidOption {
623                    option: format!("GooseDefault::{:?}", key),
624                    value: value.to_string(),
625                    detail: format!(
626                        "set_default(GooseDefault::{:?}, {}) expected bool value, received &str",
627                        key, value
628                    ),
629                });
630            }
631            GooseDefault::DebugFormat
632            | GooseDefault::ErrorFormat
633            | GooseDefault::TransactionFormat
634            | GooseDefault::ScenarioFormat
635            | GooseDefault::RequestFormat => {
636                return Err(GooseError::InvalidOption {
637                    option: format!("GooseDefault::{:?}", key),
638                    value: value.to_string(),
639                    detail: format!(
640                        "set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received &str",
641                        key, value
642                    ),
643                });
644            }
645            GooseDefault::CoordinatedOmissionMitigation => {
646                return Err(GooseError::InvalidOption {
647                    option: format!("GooseDefault::{:?}", key),
648                    value: value.to_string(),
649                    detail: format!(
650                        "set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received &str",
651                        key, value
652                    ),
653                });
654            }
655        }
656        Ok(Box::new(self))
657    }
658}
659impl GooseDefaultType<usize> for GooseAttack {
660    /// Sets [`GooseDefault`] to a [`usize`] value.
661    fn set_default(mut self, key: GooseDefault, value: usize) -> Result<Box<Self>, GooseError> {
662        match key {
663            GooseDefault::Users => self.defaults.users = Some(value),
664            GooseDefault::StartupTime => self.defaults.startup_time = Some(value),
665            GooseDefault::RunTime => self.defaults.run_time = Some(value),
666            GooseDefault::Iterations => self.defaults.iterations = Some(value),
667            GooseDefault::RunningMetrics => self.defaults.running_metrics = Some(value),
668            GooseDefault::LogLevel => self.defaults.log_level = Some(value as u8),
669            GooseDefault::Quiet => self.defaults.quiet = Some(value as u8),
670            GooseDefault::Verbose => self.defaults.verbose = Some(value as u8),
671            GooseDefault::ThrottleRequests => self.defaults.throttle_requests = Some(value),
672            GooseDefault::TelnetPort => self.defaults.telnet_port = Some(value as u16),
673            GooseDefault::WebSocketPort => self.defaults.websocket_port = Some(value as u16),
674            // Otherwise display a helpful and explicit error.
675            GooseDefault::DebugLog
676            | GooseDefault::ErrorLog
677            | GooseDefault::GooseLog
678            | GooseDefault::HatchRate
679            | GooseDefault::Host
680            | GooseDefault::ReportFile
681            | GooseDefault::RequestLog
682            | GooseDefault::ScenarioLog
683            | GooseDefault::Scenarios
684            | GooseDefault::TelnetHost
685            | GooseDefault::TestPlan
686            | GooseDefault::Timeout
687            | GooseDefault::TransactionLog
688            | GooseDefault::WebSocketHost => {
689                return Err(GooseError::InvalidOption {
690                    option: format!("GooseDefault::{:?}", key),
691                    value: format!("{}", value),
692                    detail: format!(
693                        "set_default(GooseDefault::{:?}, {}) expected &str value, received usize",
694                        key, value
695                    ),
696                })
697            }
698            GooseDefault::NoResetMetrics
699            | GooseDefault::NoMetrics
700            | GooseDefault::NoTransactionMetrics
701            | GooseDefault::NoScenarioMetrics
702            | GooseDefault::RequestBody
703            | GooseDefault::NoPrintMetrics
704            | GooseDefault::NoErrorSummary
705            | GooseDefault::NoDebugBody
706            | GooseDefault::NoTelnet
707            | GooseDefault::NoWebSocket
708            | GooseDefault::NoAutoStart
709            | GooseDefault::NoGzip
710            | GooseDefault::NoStatusCodes
711            | GooseDefault::StickyFollow
712            | GooseDefault::NoGranularData
713            | GooseDefault::AcceptInvalidCerts => {
714                return Err(GooseError::InvalidOption {
715                    option: format!("GooseDefault::{:?}", key),
716                    value: format!("{}", value),
717                    detail: format!(
718                        "set_default(GooseDefault::{:?}, {}) expected bool value, received usize",
719                        key, value
720                    ),
721                })
722            }
723            GooseDefault::RequestFormat
724            | GooseDefault::DebugFormat
725            | GooseDefault::ErrorFormat
726            | GooseDefault::ScenarioFormat
727            | GooseDefault::TransactionFormat => {
728                return Err(GooseError::InvalidOption {
729                    option: format!("GooseDefault::{:?}", key),
730                    value: value.to_string(),
731                    detail: format!(
732                        "set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received usize",
733                        key, value
734                    ),
735                });
736            }
737            GooseDefault::CoordinatedOmissionMitigation => {
738                return Err(GooseError::InvalidOption {
739                    option: format!("GooseDefault::{:?}", key),
740                    value: value.to_string(),
741                    detail: format!(
742                        "set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received usize",
743                        key, value
744                    ),
745                });
746            }
747        }
748        Ok(Box::new(self))
749    }
750}
751impl GooseDefaultType<bool> for GooseAttack {
752    /// Sets [`GooseDefault`] to a [`bool`] value.
753    fn set_default(mut self, key: GooseDefault, value: bool) -> Result<Box<Self>, GooseError> {
754        match key {
755            GooseDefault::NoResetMetrics => self.defaults.no_reset_metrics = Some(value),
756            GooseDefault::NoMetrics => self.defaults.no_metrics = Some(value),
757            GooseDefault::NoTransactionMetrics => {
758                self.defaults.no_transaction_metrics = Some(value)
759            }
760            GooseDefault::NoScenarioMetrics => self.defaults.no_scenario_metrics = Some(value),
761            GooseDefault::RequestBody => self.defaults.request_body = Some(value),
762            GooseDefault::NoPrintMetrics => self.defaults.no_print_metrics = Some(value),
763            GooseDefault::NoErrorSummary => self.defaults.no_error_summary = Some(value),
764            GooseDefault::NoDebugBody => self.defaults.no_debug_body = Some(value),
765            GooseDefault::NoTelnet => self.defaults.no_telnet = Some(value),
766            GooseDefault::NoWebSocket => self.defaults.no_websocket = Some(value),
767            GooseDefault::NoAutoStart => self.defaults.no_autostart = Some(value),
768            GooseDefault::NoGzip => self.defaults.no_gzip = Some(value),
769            GooseDefault::AcceptInvalidCerts => self.defaults.accept_invalid_certs = Some(value),
770            GooseDefault::NoStatusCodes => self.defaults.no_status_codes = Some(value),
771            GooseDefault::StickyFollow => self.defaults.sticky_follow = Some(value),
772            GooseDefault::NoGranularData => self.defaults.no_granular_report = Some(value),
773            // Otherwise display a helpful and explicit error.
774            GooseDefault::DebugLog
775            | GooseDefault::ErrorLog
776            | GooseDefault::GooseLog
777            | GooseDefault::HatchRate
778            | GooseDefault::Host
779            | GooseDefault::ReportFile
780            | GooseDefault::RequestLog
781            | GooseDefault::ScenarioLog
782            | GooseDefault::Scenarios
783            | GooseDefault::TelnetHost
784            | GooseDefault::TestPlan
785            | GooseDefault::Timeout
786            | GooseDefault::TransactionLog
787            | GooseDefault::WebSocketHost => {
788                return Err(GooseError::InvalidOption {
789                    option: format!("GooseDefault::{:?}", key),
790                    value: format!("{}", value),
791                    detail: format!(
792                        "set_default(GooseDefault::{:?}, {}) expected &str value, received bool",
793                        key, value
794                    ),
795                })
796            }
797            GooseDefault::Users
798            | GooseDefault::StartupTime
799            | GooseDefault::RunTime
800            | GooseDefault::RunningMetrics
801            | GooseDefault::Iterations
802            | GooseDefault::LogLevel
803            | GooseDefault::Quiet
804            | GooseDefault::Verbose
805            | GooseDefault::ThrottleRequests
806            | GooseDefault::TelnetPort
807            | GooseDefault::WebSocketPort => {
808                return Err(GooseError::InvalidOption {
809                    option: format!("GooseDefault::{:?}", key),
810                    value: format!("{}", value),
811                    detail: format!(
812                        "set_default(GooseDefault::{:?}, {}) expected usize value, received bool",
813                        key, value
814                    ),
815                })
816            }
817            GooseDefault::RequestFormat
818            | GooseDefault::DebugFormat
819            | GooseDefault::ErrorFormat
820            | GooseDefault::ScenarioFormat
821            | GooseDefault::TransactionFormat => {
822                return Err(GooseError::InvalidOption {
823                    option: format!("GooseDefault::{:?}", key),
824                    value: value.to_string(),
825                    detail: format!(
826                        "set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received bool",
827                        key, value
828                    ),
829                });
830            }
831            GooseDefault::CoordinatedOmissionMitigation => {
832                return Err(GooseError::InvalidOption {
833                    option: format!("GooseDefault::{:?}", key),
834                    value: value.to_string(),
835                    detail: format!(
836                        "set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received bool",
837                        key, value
838                    ),
839                });
840            }
841        }
842        Ok(Box::new(self))
843    }
844}
845impl GooseDefaultType<GooseCoordinatedOmissionMitigation> for GooseAttack {
846    /// Sets [`GooseDefault`] to a [`GooseCoordinatedOmissionMitigation`] value.
847    fn set_default(
848        mut self,
849        key: GooseDefault,
850        value: GooseCoordinatedOmissionMitigation,
851    ) -> Result<Box<Self>, GooseError> {
852        match key {
853            GooseDefault::CoordinatedOmissionMitigation => self.defaults.co_mitigation = Some(value),
854            // Otherwise display a helpful and explicit error.
855            GooseDefault::NoResetMetrics
856            | GooseDefault::NoMetrics
857            | GooseDefault::NoTransactionMetrics
858            | GooseDefault::NoScenarioMetrics
859            | GooseDefault::RequestBody
860            | GooseDefault::NoPrintMetrics
861            | GooseDefault::NoErrorSummary
862            | GooseDefault::NoDebugBody
863            | GooseDefault::NoTelnet
864            | GooseDefault::NoWebSocket
865            | GooseDefault::NoAutoStart
866            | GooseDefault::NoGzip
867            | GooseDefault::NoStatusCodes
868            | GooseDefault::StickyFollow
869            | GooseDefault::NoGranularData
870            | GooseDefault::AcceptInvalidCerts  => {
871                return Err(GooseError::InvalidOption {
872                    option: format!("GooseDefault::{:?}", key),
873                    value: format!("{:?}", value),
874                    detail: format!(
875                        "set_default(GooseDefault::{:?}, {:?}) expected bool value, received GooseCoordinatedOmissionMitigation",
876                        key, value
877                    ),
878                })
879            }
880            // Otherwise display a helpful and explicit error.
881            GooseDefault::DebugLog
882            | GooseDefault::ErrorLog
883            | GooseDefault::GooseLog
884            | GooseDefault::HatchRate
885            | GooseDefault::Host
886            | GooseDefault::ReportFile
887            | GooseDefault::RequestLog
888            | GooseDefault::ScenarioLog
889            | GooseDefault::Scenarios
890            | GooseDefault::TelnetHost
891            | GooseDefault::TestPlan
892            | GooseDefault::Timeout
893            | GooseDefault::TransactionLog
894            | GooseDefault::WebSocketHost => {
895                return Err(GooseError::InvalidOption {
896                    option: format!("GooseDefault::{:?}", key),
897                    value: format!("{:?}", value),
898                    detail: format!(
899                        "set_default(GooseDefault::{:?}, {:?}) expected &str value, received GooseCoordinatedOmissionMitigation",
900                        key, value
901                    ),
902                })
903            }
904            GooseDefault::Users
905            | GooseDefault::StartupTime
906            | GooseDefault::RunTime
907            | GooseDefault::RunningMetrics
908            | GooseDefault::Iterations
909            | GooseDefault::LogLevel
910            | GooseDefault::Quiet
911            | GooseDefault::Verbose
912            | GooseDefault::ThrottleRequests
913            | GooseDefault::TelnetPort
914            | GooseDefault::WebSocketPort => {
915                return Err(GooseError::InvalidOption {
916                    option: format!("GooseDefault::{:?}", key),
917                    value: format!("{:?}", value),
918                    detail: format!(
919                        "set_default(GooseDefault::{:?}, {:?}) expected usize value, received GooseCoordinatedOmissionMitigation",
920                        key, value
921                    ),
922                })
923            }
924            GooseDefault::RequestFormat
925            | GooseDefault::DebugFormat
926            | GooseDefault::ErrorFormat
927            | GooseDefault::ScenarioFormat
928            | GooseDefault::TransactionFormat => {
929                return Err(GooseError::InvalidOption {
930                    option: format!("GooseDefault::{:?}", key),
931                    value: format!("{:?}", value),
932                    detail: format!(
933                        "set_default(GooseDefault::{:?}, {:?}) expected GooseLogFormat value, received GooseCoordinatedOmissionMitigation",
934                        key, value
935                    ),
936                })
937            }
938        }
939        Ok(Box::new(self))
940    }
941}
942impl GooseDefaultType<GooseLogFormat> for GooseAttack {
943    /// Sets [`GooseDefault`] to a [`GooseLogFormat`] value.
944    fn set_default(
945        mut self,
946        key: GooseDefault,
947        value: GooseLogFormat,
948    ) -> Result<Box<Self>, GooseError> {
949        match key {
950            GooseDefault::RequestFormat => self.defaults.request_format = Some(value),
951            GooseDefault::DebugFormat => self.defaults.debug_format = Some(value),
952            GooseDefault::ErrorFormat => self.defaults.error_format = Some(value),
953            GooseDefault::TransactionFormat => self.defaults.transaction_format = Some(value),
954            GooseDefault::ScenarioFormat => self.defaults.scenario_format = Some(value),
955            // Otherwise display a helpful and explicit error.
956            GooseDefault::NoResetMetrics
957            | GooseDefault::NoMetrics
958            | GooseDefault::NoTransactionMetrics
959            | GooseDefault::NoScenarioMetrics
960            | GooseDefault::RequestBody
961            | GooseDefault::NoPrintMetrics
962            | GooseDefault::NoErrorSummary
963            | GooseDefault::NoDebugBody
964            | GooseDefault::NoTelnet
965            | GooseDefault::NoWebSocket
966            | GooseDefault::NoAutoStart
967            | GooseDefault::NoGzip
968            | GooseDefault::NoStatusCodes
969            | GooseDefault::StickyFollow
970            | GooseDefault::NoGranularData
971            | GooseDefault::AcceptInvalidCerts => {
972                return Err(GooseError::InvalidOption {
973                    option: format!("GooseDefault::{:?}", key),
974                    value: format!("{:?}", value),
975                    detail: format!(
976                        "set_default(GooseDefault::{:?}, {:?}) expected bool value, received GooseCoordinatedOmissionMitigation",
977                        key, value
978                    ),
979                })
980            }
981            // Otherwise display a helpful and explicit error.
982            GooseDefault::DebugLog
983            | GooseDefault::ErrorLog
984            | GooseDefault::GooseLog
985            | GooseDefault::HatchRate
986            | GooseDefault::Host
987            | GooseDefault::ReportFile
988            | GooseDefault::RequestLog
989            | GooseDefault::ScenarioLog
990            | GooseDefault::Scenarios
991            | GooseDefault::TelnetHost
992            | GooseDefault::TestPlan
993            | GooseDefault::Timeout
994            | GooseDefault::TransactionLog
995            | GooseDefault::WebSocketHost => {
996                return Err(GooseError::InvalidOption {
997                    option: format!("GooseDefault::{:?}", key),
998                    value: format!("{:?}", value),
999                    detail: format!(
1000                        "set_default(GooseDefault::{:?}, {:?}) expected &str value, received GooseCoordinatedOmissionMitigation",
1001                        key, value
1002                    ),
1003                })
1004            }
1005            GooseDefault::Users
1006            | GooseDefault::StartupTime
1007            | GooseDefault::RunTime
1008            | GooseDefault::RunningMetrics
1009            | GooseDefault::Iterations
1010            | GooseDefault::LogLevel
1011            | GooseDefault::Quiet
1012            | GooseDefault::Verbose
1013            | GooseDefault::ThrottleRequests
1014            | GooseDefault::TelnetPort
1015            | GooseDefault::WebSocketPort => {
1016                return Err(GooseError::InvalidOption {
1017                    option: format!("GooseDefault::{:?}", key),
1018                    value: format!("{:?}", value),
1019                    detail: format!(
1020                        "set_default(GooseDefault::{:?}, {:?}) expected usize value, received GooseCoordinatedOmissionMitigation",
1021                        key, value
1022                    ),
1023                })
1024            }
1025            GooseDefault::CoordinatedOmissionMitigation => {
1026                return Err(GooseError::InvalidOption {
1027                    option: format!("GooseDefault::{:?}", key),
1028                    value: format!("{:?}", value),
1029                    detail: format!(
1030                        "set_default(GooseDefault::{:?}, {:?}) expected GooseCoordinatedOmissionMitigation value, received GooseLogFormat",
1031                        key, value
1032                    ),
1033                })
1034            }
1035        }
1036        Ok(Box::new(self))
1037    }
1038}
1039
1040/// Used internally to configure [`GooseConfiguration`] values based on precedence rules.
1041#[derive(Debug, Clone)]
1042pub(crate) struct GooseValue<'a, T> {
1043    /// The optional value to set.
1044    pub(crate) value: Option<T>,
1045    /// Filter using this value if true.
1046    pub(crate) filter: bool,
1047    /// An optional INFO level Goose log message.
1048    pub(crate) message: &'a str,
1049}
1050
1051pub(crate) trait GooseConfigure<T> {
1052    /// Set [`GooseValue`] with supported type.
1053    fn get_value(&self, values: Vec<GooseValue<T>>) -> Option<T>;
1054}
1055
1056impl GooseConfigure<usize> for GooseConfiguration {
1057    /// Use [`GooseValue`] to set a [`usize`] value.
1058    fn get_value(&self, values: Vec<GooseValue<usize>>) -> Option<usize> {
1059        for value in values {
1060            if let Some(v) = value.value {
1061                if value.filter {
1062                    continue;
1063                } else {
1064                    if !value.message.is_empty() {
1065                        info!("{} = {}", value.message, v)
1066                    }
1067                    return Some(v);
1068                }
1069            }
1070        }
1071        None
1072    }
1073}
1074impl GooseConfigure<u16> for GooseConfiguration {
1075    /// Use [`GooseValue`] to set a [`u16`] value.
1076    fn get_value(&self, values: Vec<GooseValue<u16>>) -> Option<u16> {
1077        for value in values {
1078            if let Some(v) = value.value {
1079                if value.filter {
1080                    continue;
1081                } else {
1082                    if !value.message.is_empty() {
1083                        info!("{} = {}", value.message, v)
1084                    }
1085                    return Some(v);
1086                }
1087            }
1088        }
1089        None
1090    }
1091}
1092impl GooseConfigure<u8> for GooseConfiguration {
1093    /// Use [`GooseValue`] to set a [`u8`] value.
1094    fn get_value(&self, values: Vec<GooseValue<u8>>) -> Option<u8> {
1095        for value in values {
1096            if let Some(v) = value.value {
1097                if value.filter {
1098                    continue;
1099                } else {
1100                    if !value.message.is_empty() {
1101                        info!("{} = {}", value.message, v)
1102                    }
1103                    return Some(v);
1104                }
1105            }
1106        }
1107        None
1108    }
1109}
1110impl GooseConfigure<f32> for GooseConfiguration {
1111    /// Use [`GooseValue`] to set a [`f32`] value.
1112    fn get_value(&self, values: Vec<GooseValue<f32>>) -> Option<f32> {
1113        for value in values {
1114            if let Some(v) = value.value {
1115                if value.filter {
1116                    continue;
1117                } else {
1118                    if !value.message.is_empty() {
1119                        info!("{} = {}", value.message, v)
1120                    }
1121                    return Some(v);
1122                }
1123            }
1124        }
1125        None
1126    }
1127}
1128impl GooseConfigure<TestPlan> for GooseConfiguration {
1129    /// Use [`GooseValue`] to set a vec<(usize, usize)> value.
1130    fn get_value(&self, values: Vec<GooseValue<TestPlan>>) -> Option<TestPlan> {
1131        for value in values {
1132            if let Some(v) = value.value {
1133                if value.filter {
1134                    continue;
1135                } else {
1136                    if !value.message.is_empty() {
1137                        info!("{} = {:?}", value.message, v)
1138                    }
1139                    return Some(v);
1140                }
1141            }
1142        }
1143        None
1144    }
1145}
1146impl GooseConfigure<String> for GooseConfiguration {
1147    /// Use [`GooseValue`] to set a [`String`] value.
1148    fn get_value(&self, values: Vec<GooseValue<String>>) -> Option<String> {
1149        for value in values {
1150            if let Some(v) = value.value {
1151                if value.filter {
1152                    continue;
1153                } else {
1154                    if !value.message.is_empty() {
1155                        info!("{} = {}", value.message, v)
1156                    }
1157                    return Some(v);
1158                }
1159            }
1160        }
1161        None
1162    }
1163}
1164impl GooseConfigure<Vec<String>> for GooseConfiguration {
1165    /// Use [`GooseValue`] to set a [`String`] value.
1166    fn get_value(&self, values: Vec<GooseValue<Vec<String>>>) -> Option<Vec<String>> {
1167        for value in values {
1168            if let Some(v) = value.value {
1169                if value.filter {
1170                    continue;
1171                } else {
1172                    if !value.message.is_empty() {
1173                        info!("{} = {:?}", value.message, v)
1174                    }
1175                    return Some(v);
1176                }
1177            }
1178        }
1179        None
1180    }
1181}
1182impl GooseConfigure<bool> for GooseConfiguration {
1183    /// Use [`GooseValue`] to set a [`bool`] value.
1184    fn get_value(&self, values: Vec<GooseValue<bool>>) -> Option<bool> {
1185        for value in values {
1186            if let Some(v) = value.value {
1187                if value.filter {
1188                    continue;
1189                } else {
1190                    if !value.message.is_empty() {
1191                        info!("{} = {}", value.message, v)
1192                    }
1193                    return Some(v);
1194                }
1195            }
1196        }
1197        None
1198    }
1199}
1200impl GooseConfigure<GooseLogFormat> for GooseConfiguration {
1201    /// Use [`GooseValue`] to set a [`GooseLogFormat`] value.
1202    fn get_value(&self, values: Vec<GooseValue<GooseLogFormat>>) -> Option<GooseLogFormat> {
1203        for value in values {
1204            if let Some(v) = value.value {
1205                if value.filter {
1206                    continue;
1207                } else {
1208                    if !value.message.is_empty() {
1209                        info!("{} = {:?}", value.message, v)
1210                    }
1211                    return Some(v);
1212                }
1213            }
1214        }
1215        None
1216    }
1217}
1218impl GooseConfigure<GooseCoordinatedOmissionMitigation> for GooseConfiguration {
1219    /// Use [`GooseValue`] to set a [`GooseCoordinatedOmissionMitigation`] value.
1220    fn get_value(
1221        &self,
1222        values: Vec<GooseValue<GooseCoordinatedOmissionMitigation>>,
1223    ) -> Option<GooseCoordinatedOmissionMitigation> {
1224        for value in values {
1225            if let Some(v) = value.value {
1226                if value.filter {
1227                    continue;
1228                } else {
1229                    if !value.message.is_empty() {
1230                        info!("{} = {:?}", value.message, v)
1231                    }
1232                    return Some(v);
1233                }
1234            }
1235        }
1236        None
1237    }
1238}
1239impl GooseConfigure<Scenarios> for GooseConfiguration {
1240    /// Use [`GooseValue`] to set a [`Scenarios`] value.
1241    fn get_value(&self, values: Vec<GooseValue<Scenarios>>) -> Option<Scenarios> {
1242        for value in values {
1243            if let Some(v) = value.value {
1244                if value.filter {
1245                    continue;
1246                } else {
1247                    if !value.message.is_empty() {
1248                        info!("{} = {:?}", value.message, v)
1249                    }
1250                    return Some(v);
1251                }
1252            }
1253        }
1254        None
1255    }
1256}
1257
1258impl GooseConfiguration {
1259    /// Implement precedence rules for all [`GooseConfiguration`] values.
1260    pub(crate) fn configure(&mut self, defaults: &GooseDefaults) {
1261        // Configure `quiet`.
1262        self.quiet = self
1263            .get_value(vec![
1264                // Use --quiet if set.
1265                GooseValue {
1266                    value: Some(self.quiet),
1267                    filter: self.quiet == 0,
1268                    message: "",
1269                },
1270                // Otherwise use GooseDefault if set.
1271                GooseValue {
1272                    value: defaults.quiet,
1273                    filter: defaults.quiet.is_none(),
1274                    message: "",
1275                },
1276            ])
1277            .unwrap_or(0);
1278
1279        // Configure `verbose`.
1280        self.verbose = self
1281            .get_value(vec![
1282                // Use --verbose if set.
1283                GooseValue {
1284                    value: Some(self.verbose),
1285                    filter: self.verbose == 0,
1286                    message: "",
1287                },
1288                // Otherwise use GooseDefault if set.
1289                GooseValue {
1290                    value: defaults.verbose,
1291                    filter: defaults.verbose.is_none(),
1292                    message: "",
1293                },
1294            ])
1295            .unwrap_or(0);
1296
1297        // Configure `log_level`.
1298        self.log_level = self
1299            .get_value(vec![
1300                // Use --log-level if set.
1301                GooseValue {
1302                    value: Some(self.log_level),
1303                    filter: self.log_level == 0,
1304                    message: "",
1305                },
1306                // Otherwise use GooseDefault if set.
1307                GooseValue {
1308                    value: defaults.log_level,
1309                    filter: defaults.log_level.is_none(),
1310                    message: "",
1311                },
1312            ])
1313            .unwrap_or(0);
1314
1315        // Configure `goose_log`.
1316        self.goose_log = self
1317            .get_value(vec![
1318                // Use --log-level if set.
1319                GooseValue {
1320                    value: Some(self.goose_log.to_string()),
1321                    filter: self.goose_log.is_empty(),
1322                    message: "",
1323                },
1324                // Otherwise use GooseDefault if set.
1325                GooseValue {
1326                    value: defaults.goose_log.clone(),
1327                    filter: defaults.goose_log.is_none(),
1328                    message: "",
1329                },
1330            ])
1331            .unwrap_or_default();
1332
1333        // Initialize the Goose logger.
1334        self.initialize_goose_logger();
1335
1336        // Configure loggers.
1337        self.configure_loggers(defaults);
1338
1339        // Configure `test_plan` before `users` so users doesn't get assigned a default when using a test plan.
1340        self.test_plan = self.get_value(vec![
1341            // Use --test-plan if set.
1342            GooseValue {
1343                value: self.test_plan.clone(),
1344                filter: self.test_plan.is_none(),
1345                message: "test_plan",
1346            },
1347            // Otherwise use GooseDefault if set and not on Worker.
1348            GooseValue {
1349                value: defaults.test_plan.clone(),
1350                filter: defaults.test_plan.is_none(),
1351                message: "test_plan",
1352            },
1353        ]);
1354
1355        // Determine how many CPUs are available.
1356        let default_users = match std::thread::available_parallelism() {
1357            Ok(ap) => Some(ap.get()),
1358            Err(e) => {
1359                // Default to 1 user if unable to detect number of CPUs.
1360                info!("failed to detect available_parallelism: {}", e);
1361                Some(1)
1362            }
1363        };
1364
1365        // Configure `users`.
1366        self.users = self.get_value(vec![
1367            // Use --users if set.
1368            GooseValue {
1369                value: self.users,
1370                filter: false,
1371                message: "users",
1372            },
1373            // Otherwise use GooseDefault if set and not on Worker.
1374            GooseValue {
1375                value: defaults.users,
1376                filter: defaults.users.is_none(),
1377                message: "users",
1378            },
1379            // Otherwise use detected number of CPUs if not on Worker.
1380            GooseValue {
1381                value: default_users,
1382                filter: self.test_plan.is_some(),
1383                message: "users defaulted to number of CPUs",
1384            },
1385        ]);
1386
1387        // Configure `startup_time`.
1388        self.startup_time = self
1389            .get_value(vec![
1390                // Use --startup-time if set.
1391                GooseValue {
1392                    value: Some(util::parse_timespan(&self.startup_time)),
1393                    filter: util::parse_timespan(&self.startup_time) == 0,
1394                    message: "startup_time",
1395                },
1396                // Otherwise use GooseDefault if set and not on Worker.
1397                GooseValue {
1398                    value: defaults.startup_time,
1399                    filter: defaults.startup_time.is_none(),
1400                    message: "startup_time",
1401                },
1402            ])
1403            .map_or_else(|| "0".to_string(), |v| v.to_string());
1404
1405        // Configure `run_time`.
1406        self.run_time = self
1407            .get_value(vec![
1408                // Use --run-time if set.
1409                GooseValue {
1410                    value: Some(util::parse_timespan(&self.run_time)),
1411                    filter: util::parse_timespan(&self.run_time) == 0,
1412                    message: "run_time",
1413                },
1414                // Otherwise use GooseDefault if set and not on Worker.
1415                GooseValue {
1416                    value: defaults.run_time,
1417                    filter: defaults.run_time.is_none(),
1418                    message: "run_time",
1419                },
1420            ])
1421            .map_or_else(|| "0".to_string(), |v| v.to_string());
1422
1423        // Configure `hatch_rate`.
1424        self.hatch_rate = self
1425            .get_value(vec![
1426                // Use --hatch-rate if set.
1427                GooseValue {
1428                    value: Some(util::get_hatch_rate(self.hatch_rate.clone())),
1429                    filter: self.hatch_rate.is_none(),
1430                    message: "hatch_rate",
1431                },
1432                // Otherwise use GooseDefault if set and not on Worker.
1433                GooseValue {
1434                    value: Some(util::get_hatch_rate(defaults.hatch_rate.clone())),
1435                    filter: defaults.hatch_rate.is_none(),
1436                    message: "hatch_rate",
1437                },
1438            ])
1439            .map(|v| v.to_string());
1440
1441        // Configure `timeout`.
1442        self.timeout = self
1443            .get_value(vec![
1444                // Use --timeout if set.
1445                GooseValue {
1446                    value: util::get_float_from_string(self.timeout.clone()),
1447                    filter: self.timeout.is_none(),
1448                    message: "timeout",
1449                },
1450                // Otherwise use GooseDefault if set and not on Worker.
1451                GooseValue {
1452                    value: util::get_float_from_string(defaults.timeout.clone()),
1453                    filter: defaults.timeout.is_none(),
1454                    message: "timeout",
1455                },
1456            ])
1457            .map(|v| v.to_string());
1458
1459        // Configure `running_metrics`.
1460        self.running_metrics = self.get_value(vec![
1461            // Use --running-metrics if set.
1462            GooseValue {
1463                value: self.running_metrics,
1464                filter: self.running_metrics.is_none(),
1465                message: "running_metrics",
1466            },
1467            // Otherwise use GooseDefault if set.
1468            GooseValue {
1469                value: defaults.running_metrics,
1470                filter: defaults.running_metrics.is_none(),
1471                message: "running_metrics",
1472            },
1473        ]);
1474
1475        // Configure `no_reset_metrics`.
1476        self.no_reset_metrics = self
1477            .get_value(vec![
1478                // Use --no-reset-metrics if set.
1479                GooseValue {
1480                    value: Some(self.no_reset_metrics),
1481                    filter: !self.no_reset_metrics,
1482                    message: "no_reset_metrics",
1483                },
1484                // Otherwise use GooseDefault if set.
1485                GooseValue {
1486                    value: defaults.no_reset_metrics,
1487                    filter: defaults.no_reset_metrics.is_none(),
1488                    message: "no_reset_metrics",
1489                },
1490            ])
1491            .unwrap_or(false);
1492
1493        // Configure `no_metrics`.
1494        self.no_metrics = self
1495            .get_value(vec![
1496                // Use --no-metrics if set.
1497                GooseValue {
1498                    value: Some(self.no_metrics),
1499                    filter: !self.no_metrics,
1500                    message: "no_metrics",
1501                },
1502                // Otherwise use GooseDefault if set.
1503                GooseValue {
1504                    value: defaults.no_metrics,
1505                    filter: defaults.no_metrics.is_none(),
1506                    message: "no_metrics",
1507                },
1508            ])
1509            .unwrap_or(false);
1510
1511        // Configure `no_transaction_metrics`.
1512        self.no_transaction_metrics = self
1513            .get_value(vec![
1514                // Use --no-transaction-metrics if set.
1515                GooseValue {
1516                    value: Some(self.no_transaction_metrics),
1517                    filter: !self.no_transaction_metrics,
1518                    message: "no_transaction_metrics",
1519                },
1520                // Otherwise use GooseDefault if set.
1521                GooseValue {
1522                    value: defaults.no_transaction_metrics,
1523                    filter: defaults.no_transaction_metrics.is_none(),
1524                    message: "no_transaction_metrics",
1525                },
1526            ])
1527            .unwrap_or(false);
1528
1529        // Configure `no_scenario_metrics`.
1530        self.no_scenario_metrics = self
1531            .get_value(vec![
1532                // Use --no-scenario-metrics if set.
1533                GooseValue {
1534                    value: Some(self.no_scenario_metrics),
1535                    filter: !self.no_scenario_metrics,
1536                    message: "no_scenario_metrics",
1537                },
1538                // Otherwise use GooseDefault if set.
1539                GooseValue {
1540                    value: defaults.no_scenario_metrics,
1541                    filter: defaults.no_scenario_metrics.is_none(),
1542                    message: "no_scenario_metrics",
1543                },
1544            ])
1545            .unwrap_or(false);
1546
1547        // Configure `no_print_metrics`.
1548        self.no_print_metrics = self
1549            .get_value(vec![
1550                // Use --no-print-metrics if set.
1551                GooseValue {
1552                    value: Some(self.no_print_metrics),
1553                    filter: !self.no_print_metrics,
1554                    message: "no_print_metrics",
1555                },
1556                // Otherwise use GooseDefault if set.
1557                GooseValue {
1558                    value: defaults.no_print_metrics,
1559                    filter: defaults.no_print_metrics.is_none(),
1560                    message: "no_print_metrics",
1561                },
1562            ])
1563            .unwrap_or(false);
1564
1565        // Configure `no_error_summary`.
1566        self.no_error_summary = self
1567            .get_value(vec![
1568                // Use --no-error-summary if set.
1569                GooseValue {
1570                    value: Some(self.no_error_summary),
1571                    filter: !self.no_error_summary,
1572                    message: "no_error_summary",
1573                },
1574                // Otherwise use GooseDefault if set.
1575                GooseValue {
1576                    value: defaults.no_error_summary,
1577                    filter: defaults.no_error_summary.is_none(),
1578                    message: "no_error_summary",
1579                },
1580            ])
1581            .unwrap_or(false);
1582
1583        // Configure `report_file`.
1584        self.report_file = self
1585            .get_value(vec![
1586                // Use --report-file if set.
1587                GooseValue {
1588                    value: Some(self.report_file.clone()),
1589                    filter: self.report_file.is_empty(),
1590                    message: "report_file",
1591                },
1592                // Otherwise use GooseDefault if set.
1593                GooseValue {
1594                    value: defaults.report_file.clone(),
1595                    filter: defaults.report_file.is_none(),
1596                    message: "report_file",
1597                },
1598            ])
1599            .unwrap_or_default();
1600
1601        // Configure `no_granular_report`.
1602        self.no_debug_body = self
1603            .get_value(vec![
1604                // Use --no-granular-report if set.
1605                GooseValue {
1606                    value: Some(self.no_granular_report),
1607                    filter: !self.no_granular_report,
1608                    message: "no_granular_report",
1609                },
1610                // Otherwise use GooseDefault if set.
1611                GooseValue {
1612                    value: defaults.no_debug_body,
1613                    filter: defaults.no_debug_body.is_none(),
1614                    message: "no_granular_report",
1615                },
1616            ])
1617            .unwrap_or(false);
1618
1619        // Configure `iterations`.
1620        self.iterations = self
1621            .get_value(vec![
1622                // Use --iterations if set.
1623                GooseValue {
1624                    value: Some(self.iterations),
1625                    filter: false,
1626                    message: "iterations",
1627                },
1628                // Use GooseDefault if not already set and not Worker.
1629                GooseValue {
1630                    value: defaults.iterations,
1631                    filter: defaults.iterations.is_none(),
1632                    message: "iterations",
1633                },
1634            ])
1635            .unwrap_or(0);
1636
1637        // Configure `scenarios`.
1638        self.scenarios = self
1639            .get_value(vec![
1640                // Use --scenarios if set.
1641                GooseValue {
1642                    value: Some(self.scenarios.clone()),
1643                    filter: self.scenarios.active.is_empty(),
1644                    message: "scenarios",
1645                },
1646                // Use GooseDefault if not already set and not Worker.
1647                GooseValue {
1648                    value: defaults.scenarios.clone(),
1649                    filter: defaults.scenarios.is_none(),
1650                    message: "scenarios",
1651                },
1652            ])
1653            .unwrap_or_else(|| Scenarios { active: Vec::new() });
1654
1655        // Configure `no_debug_body`.
1656        self.no_debug_body = self
1657            .get_value(vec![
1658                // Use --no-debug-body if set.
1659                GooseValue {
1660                    value: Some(self.no_debug_body),
1661                    filter: !self.no_debug_body,
1662                    message: "no_debug_body",
1663                },
1664                // Otherwise use GooseDefault if set.
1665                GooseValue {
1666                    value: defaults.no_debug_body,
1667                    filter: defaults.no_debug_body.is_none(),
1668                    message: "no_debug_body",
1669                },
1670            ])
1671            .unwrap_or(false);
1672
1673        // Configure `no_status_codes`.
1674        self.no_status_codes = self
1675            .get_value(vec![
1676                // Use --no-status-codes if set.
1677                GooseValue {
1678                    value: Some(self.no_status_codes),
1679                    filter: !self.no_status_codes,
1680                    message: "no_status_codes",
1681                },
1682                // Otherwise use GooseDefault if set.
1683                GooseValue {
1684                    value: defaults.no_status_codes,
1685                    filter: defaults.no_status_codes.is_none(),
1686                    message: "no_status_codes",
1687                },
1688            ])
1689            .unwrap_or(false);
1690
1691        // Configure `no_telnet`.
1692        self.no_telnet = self
1693            .get_value(vec![
1694                // Use --no-telnet if set.
1695                GooseValue {
1696                    value: Some(self.no_telnet),
1697                    filter: !self.no_telnet,
1698                    message: "no_telnet",
1699                },
1700                // Use default if --no-telnet hasn't been set.
1701                GooseValue {
1702                    value: defaults.no_telnet,
1703                    filter: defaults.no_telnet.is_none(),
1704                    message: "",
1705                },
1706            ])
1707            .unwrap_or(false);
1708
1709        // Configure `no_websocket`.
1710        self.no_websocket = self
1711            .get_value(vec![
1712                // Use --no-websocket if set.
1713                GooseValue {
1714                    value: Some(self.no_websocket),
1715                    filter: !self.no_websocket,
1716                    message: "no_websocket",
1717                },
1718                // Use default if --no-websocket hasn't been set.
1719                GooseValue {
1720                    value: defaults.no_websocket,
1721                    filter: defaults.no_websocket.is_none(),
1722                    message: "",
1723                },
1724            ])
1725            .unwrap_or(false);
1726
1727        // Configure `no_autostart`.
1728        self.no_autostart = self
1729            .get_value(vec![
1730                // Use --no-autostart if set.
1731                GooseValue {
1732                    value: Some(self.no_autostart),
1733                    filter: !self.no_autostart,
1734                    message: "no_autostart",
1735                },
1736                // Use GooseDefault if not already set and not Worker.
1737                GooseValue {
1738                    value: defaults.no_autostart,
1739                    filter: defaults.no_autostart.is_none(),
1740                    message: "no_autostart",
1741                },
1742            ])
1743            .unwrap_or(false);
1744
1745        // Configure `no_gzip`.
1746        self.no_gzip = self
1747            .get_value(vec![
1748                // Use --no-gzip if set.
1749                GooseValue {
1750                    value: Some(self.no_gzip),
1751                    filter: !self.no_gzip,
1752                    message: "no_gzip",
1753                },
1754                // Use GooseDefault if not already set and not Worker.
1755                GooseValue {
1756                    value: defaults.no_gzip,
1757                    filter: defaults.no_gzip.is_none(),
1758                    message: "no_gzip",
1759                },
1760            ])
1761            .unwrap_or(false);
1762
1763        // Configure `accept_invalid_certs`
1764        self.accept_invalid_certs = self
1765            .get_value(vec![
1766                // Use --accept-invalid-certs if set.
1767                GooseValue {
1768                    value: Some(self.accept_invalid_certs),
1769                    filter: !self.accept_invalid_certs,
1770                    message: "accept_invalid_certs",
1771                },
1772                // Use GooseDefault if not already set and not Worker.
1773                GooseValue {
1774                    value: defaults.accept_invalid_certs,
1775                    filter: defaults.accept_invalid_certs.is_none(),
1776                    message: "accept_invalid_certs",
1777                },
1778            ])
1779            .unwrap_or(false);
1780
1781        self.co_mitigation = self.get_value(vec![
1782            // Use --co-mitigation if set.
1783            GooseValue {
1784                value: self.co_mitigation.clone(),
1785                filter: self.co_mitigation.is_none(),
1786                message: "co_mitigation",
1787            },
1788            // Otherwise use GooseDefault if set and not Worker.
1789            GooseValue {
1790                value: defaults.co_mitigation.clone(),
1791                filter: defaults.co_mitigation.is_none(),
1792                message: "co_mitigation",
1793            },
1794            // Otherwise default to GooseCoordinaatedOmissionMitigation::Disabled.
1795            GooseValue {
1796                value: Some(GooseCoordinatedOmissionMitigation::Disabled),
1797                filter: false,
1798                message: "",
1799            },
1800        ]);
1801
1802        // Configure `throttle_requests`.
1803        self.throttle_requests = self
1804            .get_value(vec![
1805                // Use --throttle-requests if set.
1806                GooseValue {
1807                    value: Some(self.throttle_requests),
1808                    filter: self.throttle_requests == 0,
1809                    message: "throttle_requests",
1810                },
1811                // Otherwise use GooseDefault if set.
1812                GooseValue {
1813                    value: defaults.throttle_requests,
1814                    filter: defaults.throttle_requests.is_none(),
1815                    message: "throttle_requests",
1816                },
1817            ])
1818            .unwrap_or(0);
1819
1820        // Configure `sticky_follow`.
1821        self.sticky_follow = self
1822            .get_value(vec![
1823                // Use --sticky-follow if set.
1824                GooseValue {
1825                    value: Some(self.sticky_follow),
1826                    filter: !self.sticky_follow,
1827                    message: "sticky_follow",
1828                },
1829                // Use GooseDefault if not already set and not Worker.
1830                GooseValue {
1831                    value: defaults.sticky_follow,
1832                    filter: defaults.sticky_follow.is_none(),
1833                    message: "sticky_follow",
1834                },
1835            ])
1836            .unwrap_or(false);
1837    }
1838
1839    /// Validate configured [`GooseConfiguration`] values.
1840    pub(crate) fn validate(&self) -> Result<(), GooseError> {
1841        // Can't set both --verbose and --quiet.
1842        if self.verbose > 0 && self.quiet > 0 {
1843            return Err(GooseError::InvalidOption {
1844                option: "`configuration.verbose`".to_string(),
1845                value: self.verbose.to_string(),
1846                detail: "`configuration.verbose` can not be set with `configuration.quiet`."
1847                    .to_string(),
1848            });
1849        }
1850
1851        // If set, hatch rate must be non-zero.
1852        if let Some(hatch_rate) = self.hatch_rate.as_ref() {
1853            if hatch_rate == "0" {
1854                return Err(GooseError::InvalidOption {
1855                    option: "`configuration.hatch_rate`".to_string(),
1856                    value: hatch_rate.to_string(),
1857                    detail: "`configuration.hatch_rate` must be set to at least 1.".to_string(),
1858                });
1859            }
1860        }
1861
1862        // If set, timeout must be greater than zero.
1863        if let Some(timeout) = self.timeout.as_ref() {
1864            if crate::util::get_float_from_string(self.timeout.clone())
1865                .expect("failed to re-convert string to float")
1866                <= 0.0
1867            {
1868                return Err(GooseError::InvalidOption {
1869                    option: "`configuration.timeout`".to_string(),
1870                    value: timeout.to_string(),
1871                    detail: "`configuration.timeout` must be greater than 0.".to_string(),
1872                });
1873            }
1874        }
1875
1876        // Validate `users`.
1877        if let Some(users) = self.users.as_ref() {
1878            if users == &0 {
1879                return Err(GooseError::InvalidOption {
1880                    option: "configuration.users".to_string(),
1881                    value: users.to_string(),
1882                    detail: "`configuration.users` must be set to at least 1.".to_string(),
1883                });
1884            }
1885        }
1886
1887        // Validate `startup_time`.
1888        if self.startup_time != "0" {
1889            // Startup time can't be set with hatch rate.
1890            if self.hatch_rate.is_some() {
1891                return Err(GooseError::InvalidOption {
1892                    option: "`configuration.startup_time`".to_string(),
1893                    value: self.startup_time.to_string(),
1894                    detail: "`configuration.startup_time` can not be set with `configuration.hatch_rate`.".to_string(),
1895                });
1896            }
1897
1898            // Startup time requires at least 2 users.
1899            if let Some(users) = self.users.as_ref() {
1900                if users < &2 {
1901                    return Err(GooseError::InvalidOption {
1902                        option: "configuration.users".to_string(),
1903                        value: users.to_string(),
1904                        detail: "`configuration.users` must be set to at least 2 when `configuration.startup_time` is set.".to_string(),
1905                    });
1906                }
1907            }
1908        }
1909
1910        // Validate `test_plan`.
1911        if self.test_plan.is_some() {
1912            // The --users option isn't compatible with --test-plan.
1913            if let Some(users) = self.users.as_ref() {
1914                return Err(GooseError::InvalidOption {
1915                    option: "`configuration.users`".to_string(),
1916                    value: users.to_string(),
1917                    detail: "`configuration.users` can not be set with `configuration.test_plan`."
1918                        .to_string(),
1919                });
1920            }
1921            // The --startup-time option isn't compatible with --test-plan.
1922            if self.startup_time != "0" {
1923                return Err(GooseError::InvalidOption {
1924                    option: "`configuration.startup_time`".to_string(),
1925                    value: self.startup_time.to_string(),
1926                    detail: "`configuration.startup_time` can not be set with `configuration.test_plan`.".to_string(),
1927                });
1928            }
1929            // The --hatch-rate option isn't compatible with --test-plan.
1930            if let Some(hatch_rate) = self.hatch_rate.as_ref() {
1931                return Err(GooseError::InvalidOption {
1932                    option: "`configuration.hatch_rate`".to_string(),
1933                    value: hatch_rate.to_string(),
1934                    detail:
1935                        "`configuration.hatch_rate` can not be set with `configuration.test_plan`."
1936                            .to_string(),
1937                });
1938            }
1939            // The --run-time option isn't compatible with --test-plan.
1940            if self.run_time != "0" {
1941                return Err(GooseError::InvalidOption {
1942                    option: "`configuration.run_time`".to_string(),
1943                    value: self.run_time.to_string(),
1944                    detail:
1945                        "`configuration.run_time` can not be set with `configuration.test_plan`."
1946                            .to_string(),
1947                });
1948            }
1949            // The --no-reset-metrics option isn't compatible with --test-plan.
1950            if self.no_reset_metrics {
1951                return Err(GooseError::InvalidOption {
1952                    option: "`configuration.no_reset_metrics".to_string(),
1953                    value: self.no_reset_metrics.to_string(),
1954                    detail: "`configuration.no_reset_metrics` can not be set with `configuration.test_plan` (metrics are not reset)."
1955                        .to_string(),
1956                });
1957            }
1958        }
1959
1960        // Validate `iterations`.
1961        if self.iterations > 0 {
1962            // The --run-time option isn't compatible with --iterations.
1963            if self.run_time != "0" {
1964                return Err(GooseError::InvalidOption {
1965                    option: "`configuration.run_time`".to_string(),
1966                    value: self.run_time.to_string(),
1967                    detail:
1968                        "`configuration.run_time` can not be set with `configuration.iterations`."
1969                            .to_string(),
1970                });
1971            }
1972            // The --test-plan option isn't compatible with --iterations.
1973            if self.test_plan.is_some() {
1974                return Err(GooseError::InvalidOption {
1975                    option: "`configuration.iterations`".to_string(),
1976                    value: self.iterations.to_string(),
1977                    detail:
1978                        "`configuration.iteratoins` can not be set with `configuration.test_plan`."
1979                            .to_string(),
1980                });
1981            }
1982            // The --no-reset-metrics option isn't compatible with --iterations.
1983            if self.no_reset_metrics {
1984                return Err(GooseError::InvalidOption {
1985                    option: "`configuration.no_reset_metrics".to_string(),
1986                    value: self.no_reset_metrics.to_string(),
1987                    detail: "`configuration.no_reset_metrics` can not be set with `configuration.iterations` (metrics are not reset)."
1988                        .to_string(),
1989                });
1990            }
1991        }
1992
1993        // Validate `no_metrics`.
1994        if self.no_metrics {
1995            // Request log can't be written if metrics are disabled.
1996            if !self.request_log.is_empty() {
1997                return Err(GooseError::InvalidOption {
1998                    option: "`configuration.request_log`".to_string(),
1999                    value: self.request_log.to_string(),
2000                    detail: "`configuration.request_log` can not be set with `configuration.no_metrics`.".to_string(),
2001                });
2002            // Transaction log can't be written if metrics are disabled.
2003            } else if !self.transaction_log.is_empty() {
2004                return Err(GooseError::InvalidOption {
2005                    option: "`configuration.transaction_log`".to_string(),
2006                    value: self.transaction_log.to_string(),
2007                    detail:
2008                        "`configuration.transaction_log` can not be set with `configuration.no_metrics`."
2009                            .to_string(),
2010                });
2011            // Scenario log can't be written if metrics are disabled.
2012            } else if !self.scenario_log.is_empty() {
2013                return Err(GooseError::InvalidOption {
2014                    option: "`configuration.scenario_log`".to_string(),
2015                    value: self.scenario_log.to_string(),
2016                    detail:
2017                        "`configuration.scenario_log` can not be set with `configuration.no_metrics`."
2018                            .to_string(),
2019                });
2020            // Error log can't be written if metrics are disabled.
2021            } else if !self.error_log.is_empty() {
2022                return Err(GooseError::InvalidOption {
2023                    option: "`configuration.error_log`".to_string(),
2024                    value: self.error_log.to_string(),
2025                    detail:
2026                        "`configuration.error_log` can not be set with `configuration.no_metrics`."
2027                            .to_string(),
2028                });
2029            // Report file can't be written if metrics are disabled.
2030            } else if !self.report_file.is_empty() {
2031                return Err(GooseError::InvalidOption {
2032                    option: "`configuration.report_file`".to_string(),
2033                    value: format!("{:?}", self.report_file),
2034                    detail:
2035                        "`configuration.report_file` can not be set with `configuration.no_metrics`."
2036                            .to_string(),
2037                });
2038            // Coordinated Omission Mitigation can't be enabled if metrics are disabled.
2039            } else if self.co_mitigation.as_ref().unwrap()
2040                != &GooseCoordinatedOmissionMitigation::Disabled
2041            {
2042                return Err(GooseError::InvalidOption {
2043                    option: "`configuration.co_mitigation`".to_string(),
2044                    value: format!("{:?}", self.co_mitigation.as_ref().unwrap()),
2045                    detail: "`configuration.co_mitigation` can not be set with `configuration.no_metrircs`."
2046                        .to_string(),
2047                });
2048            }
2049        }
2050
2051        if self.report_file.is_empty() && self.no_granular_report {
2052            return Err(GooseError::InvalidOption {
2053                option: "`configuration.no_granular_report`".to_string(),
2054                value: true.to_string(),
2055                detail:
2056                    "`configuration.no_granular_report` can not be set without `configuration.report_file`."
2057                        .to_string(),
2058            });
2059        }
2060
2061        // Can't disable autostart if there's no Controller enabled.
2062        if self.no_autostart && self.no_telnet && self.no_websocket {
2063            return Err(GooseError::InvalidOption {
2064                option: "`configuration.no_autostart`".to_string(),
2065                value: true.to_string(),
2066                detail: "`configuration.no_autostart` requires at least one Controller be enabled"
2067                    .to_string(),
2068            });
2069        }
2070
2071        /* @TODO:
2072        if let Some(co_mitigation) = self.co_mitigation.as_ref() {
2073            if co_mitigation != &GooseCoordinatedOmissionMitigation::Disabled
2074                && self.scheduler == GooseScheduler::Random
2075            {
2076                // Coordinated Omission Mitigation is not possible together with the random scheduler,
2077                // as it's impossible to calculate an accurate request cadence.
2078                return Err(GooseError::InvalidOption {
2079                    option: key.to_string(),
2080                    value: format!("{:?}", value),
2081                    detail: format!(
2082                        "{} can not be set together with GooseScheduler::Random.",
2083                        key
2084                    ),
2085                });
2086            }
2087        }
2088        */
2089
2090        if self.throttle_requests > 0 {
2091            // Be sure throttle_requests is in allowed range.
2092            if self.throttle_requests == 0 {
2093                return Err(GooseError::InvalidOption {
2094                    option: "`configuration.throttle_requests`".to_string(),
2095                    value: self.throttle_requests.to_string(),
2096                    detail: "`configuration.throttle_requests` must be set to at least 1 request per second.".to_string(),
2097                });
2098            } else if self.throttle_requests > 1_000_000 {
2099                return Err(GooseError::InvalidOption {
2100                    option: "`configuration.throttle_requests`".to_string(),
2101                    value: self.throttle_requests.to_string(),
2102                    detail: "`configuration.throttle_requests` can not be set to more than 1,000,000 request per second.".to_string(),
2103                });
2104            }
2105        }
2106
2107        Ok(())
2108    }
2109
2110    /// Optionally initialize the Goose logger which writes to standard out and/or to
2111    /// a configurable log file.
2112    pub(crate) fn initialize_goose_logger(&self) {
2113        // Configure debug output level.
2114        let debug_level = match self.verbose {
2115            0 => match self.quiet {
2116                0 => LevelFilter::Info,
2117                _ => LevelFilter::Warn,
2118            },
2119            1 => LevelFilter::Debug,
2120            _ => LevelFilter::Trace,
2121        };
2122
2123        // Configure Goose log level.
2124        let log_level = match self.log_level {
2125            0 => LevelFilter::Warn,
2126            1 => LevelFilter::Info,
2127            2 => LevelFilter::Debug,
2128            _ => LevelFilter::Trace,
2129        };
2130
2131        // Open the log file if configured.
2132        let goose_log: Option<PathBuf> = if !self.goose_log.is_empty() {
2133            Some(PathBuf::from(&self.goose_log))
2134        // Otherwise disable the log.
2135        } else {
2136            None
2137        };
2138
2139        if let Some(log_to_file) = goose_log {
2140            match CombinedLogger::init(vec![
2141                SimpleLogger::new(debug_level, Config::default()),
2142                WriteLogger::new(
2143                    log_level,
2144                    Config::default(),
2145                    std::fs::File::create(&log_to_file).unwrap(),
2146                ),
2147            ]) {
2148                Ok(_) => (),
2149                Err(e) => {
2150                    info!("failed to initialize CombinedLogger: {}", e);
2151                }
2152            }
2153            info!("Writing to log file: {}", log_to_file.display());
2154        } else {
2155            match CombinedLogger::init(vec![SimpleLogger::new(debug_level, Config::default())]) {
2156                Ok(_) => (),
2157                Err(e) => {
2158                    info!("failed to initialize CombinedLogger: {}", e);
2159                }
2160            }
2161        }
2162
2163        info!("Output verbosity level: {}", debug_level);
2164        info!("Logfile verbosity level: {}", log_level);
2165    }
2166}
2167
2168#[cfg(test)]
2169mod test {
2170    use super::*;
2171
2172    #[test]
2173    fn set_defaults() {
2174        let host = "http://example.com/".to_string();
2175        let users: usize = 10;
2176        let run_time: usize = 10;
2177        let hatch_rate = "2".to_string();
2178        let timeout = "45".to_string();
2179        let log_level: usize = 1;
2180        let goose_log = "custom-goose.log".to_string();
2181        let quiet: usize = 0;
2182        let verbose: usize = 0;
2183        let report_file = "custom-goose-report.html".to_string();
2184        let request_log = "custom-goose-request.log".to_string();
2185        let transaction_log = "custom-goose-transaction.log".to_string();
2186        let scenario_log = "custom-goose-scenario.log".to_string();
2187        let debug_log = "custom-goose-debug.log".to_string();
2188        let error_log = "custom-goose-error.log".to_string();
2189        let throttle_requests: usize = 25;
2190
2191        let goose_attack = GooseAttack::initialize()
2192            .unwrap()
2193            .set_default(GooseDefault::Host, host.as_str())
2194            .unwrap()
2195            .set_default(GooseDefault::Users, users)
2196            .unwrap()
2197            .set_default(GooseDefault::RunTime, run_time)
2198            .unwrap()
2199            .set_default(GooseDefault::HatchRate, hatch_rate.as_str())
2200            .unwrap()
2201            .set_default(GooseDefault::LogLevel, log_level)
2202            .unwrap()
2203            .set_default(GooseDefault::GooseLog, goose_log.as_str())
2204            .unwrap()
2205            .set_default(GooseDefault::Quiet, quiet)
2206            .unwrap()
2207            .set_default(GooseDefault::Verbose, verbose)
2208            .unwrap()
2209            .set_default(GooseDefault::Timeout, timeout.as_str())
2210            .unwrap()
2211            .set_default(GooseDefault::RunningMetrics, 15)
2212            .unwrap()
2213            .set_default(GooseDefault::NoResetMetrics, true)
2214            .unwrap()
2215            .set_default(GooseDefault::NoMetrics, true)
2216            .unwrap()
2217            .set_default(GooseDefault::NoTransactionMetrics, true)
2218            .unwrap()
2219            .set_default(GooseDefault::NoScenarioMetrics, true)
2220            .unwrap()
2221            .set_default(GooseDefault::NoPrintMetrics, true)
2222            .unwrap()
2223            .set_default(GooseDefault::NoErrorSummary, true)
2224            .unwrap()
2225            .set_default(GooseDefault::NoTelnet, true)
2226            .unwrap()
2227            .set_default(GooseDefault::NoWebSocket, true)
2228            .unwrap()
2229            .set_default(GooseDefault::NoAutoStart, true)
2230            .unwrap()
2231            .set_default(GooseDefault::NoGzip, true)
2232            .unwrap()
2233            .set_default(GooseDefault::ReportFile, report_file.as_str())
2234            .unwrap()
2235            .set_default(GooseDefault::RequestLog, request_log.as_str())
2236            .unwrap()
2237            .set_default(GooseDefault::RequestFormat, GooseLogFormat::Raw)
2238            .unwrap()
2239            .set_default(GooseDefault::RequestBody, true)
2240            .unwrap()
2241            .set_default(GooseDefault::TransactionLog, transaction_log.as_str())
2242            .unwrap()
2243            .set_default(GooseDefault::TransactionFormat, GooseLogFormat::Raw)
2244            .unwrap()
2245            .set_default(GooseDefault::ScenarioLog, scenario_log.as_str())
2246            .unwrap()
2247            .set_default(GooseDefault::ScenarioFormat, GooseLogFormat::Raw)
2248            .unwrap()
2249            .set_default(GooseDefault::ErrorLog, error_log.as_str())
2250            .unwrap()
2251            .set_default(GooseDefault::ErrorFormat, GooseLogFormat::Csv)
2252            .unwrap()
2253            .set_default(GooseDefault::DebugLog, debug_log.as_str())
2254            .unwrap()
2255            .set_default(GooseDefault::DebugFormat, GooseLogFormat::Csv)
2256            .unwrap()
2257            .set_default(GooseDefault::NoDebugBody, true)
2258            .unwrap()
2259            .set_default(GooseDefault::NoStatusCodes, true)
2260            .unwrap()
2261            .set_default(
2262                GooseDefault::CoordinatedOmissionMitigation,
2263                GooseCoordinatedOmissionMitigation::Disabled,
2264            )
2265            .unwrap()
2266            .set_default(GooseDefault::ThrottleRequests, throttle_requests)
2267            .unwrap()
2268            .set_default(GooseDefault::StickyFollow, true)
2269            .unwrap();
2270
2271        assert!(goose_attack.defaults.host == Some(host));
2272        assert!(goose_attack.defaults.users == Some(users));
2273        assert!(goose_attack.defaults.run_time == Some(run_time));
2274        assert!(goose_attack.defaults.hatch_rate == Some(hatch_rate));
2275        assert!(goose_attack.defaults.log_level == Some(log_level as u8));
2276        assert!(goose_attack.defaults.goose_log == Some(goose_log));
2277        assert!(goose_attack.defaults.request_body == Some(true));
2278        assert!(goose_attack.defaults.no_debug_body == Some(true));
2279        assert!(goose_attack.defaults.quiet == Some(quiet as u8));
2280        assert!(goose_attack.defaults.verbose == Some(verbose as u8));
2281        assert!(goose_attack.defaults.running_metrics == Some(15));
2282        assert!(goose_attack.defaults.no_reset_metrics == Some(true));
2283        assert!(goose_attack.defaults.no_metrics == Some(true));
2284        assert!(goose_attack.defaults.no_transaction_metrics == Some(true));
2285        assert!(goose_attack.defaults.no_scenario_metrics == Some(true));
2286        assert!(goose_attack.defaults.no_print_metrics == Some(true));
2287        assert!(goose_attack.defaults.no_error_summary == Some(true));
2288        assert!(goose_attack.defaults.no_telnet == Some(true));
2289        assert!(goose_attack.defaults.no_websocket == Some(true));
2290        assert!(goose_attack.defaults.no_autostart == Some(true));
2291        assert!(goose_attack.defaults.timeout == Some(timeout));
2292        assert!(goose_attack.defaults.no_gzip == Some(true));
2293        assert!(goose_attack.defaults.report_file == Some(vec![report_file]));
2294        assert!(goose_attack.defaults.request_log == Some(request_log));
2295        assert!(goose_attack.defaults.request_format == Some(GooseLogFormat::Raw));
2296        assert!(goose_attack.defaults.error_log == Some(error_log));
2297        assert!(goose_attack.defaults.error_format == Some(GooseLogFormat::Csv));
2298        assert!(goose_attack.defaults.debug_log == Some(debug_log));
2299        assert!(goose_attack.defaults.debug_format == Some(GooseLogFormat::Csv));
2300        assert!(goose_attack.defaults.no_status_codes == Some(true));
2301        assert!(
2302            goose_attack.defaults.co_mitigation
2303                == Some(GooseCoordinatedOmissionMitigation::Disabled)
2304        );
2305        assert!(goose_attack.defaults.throttle_requests == Some(throttle_requests));
2306        assert!(goose_attack.defaults.sticky_follow == Some(true));
2307    }
2308}