Skip to main content

huawei_dongle_api/
config.rs

1//! Configuration for the Huawei Dongle API client
2//!
3//! This module provides configuration options for customizing the behavior of the API client.
4//!
5//! # Examples
6//!
7//! ## Using Default Configuration
8//!
9//! ```
10//! use huawei_dongle_api::Config;
11//!
12//! let config = Config::default();
13//! // Uses http://192.168.8.1 with 30s timeout and 3 retries
14//! ```
15//!
16//! ## Custom Configuration
17//!
18//! ```
19//! use huawei_dongle_api::Config;
20//! use std::time::Duration;
21//!
22//! let config = Config::builder()
23//!     .base_url("http://192.168.1.1")
24//!     .timeout(Duration::from_secs(60))
25//!     .max_retries(5)
26//!     .retry_delay(Duration::from_millis(100))
27//!     .max_retry_delay(Duration::from_secs(10))
28//!     .user_agent("MyApp/1.0")
29//!     .build();
30//! ```
31//!
32//! ## Quick Configuration for URL
33//!
34//! ```
35//! use huawei_dongle_api::Config;
36//!
37//! let config = Config::for_url("http://192.168.62.1").unwrap();
38//! ```
39
40use crate::error::{Error, Result};
41use std::time::Duration;
42use url::Url;
43
44/// Configuration for the Huawei Dongle API client.
45///
46/// Controls connection parameters, retry behavior, and HTTP settings.
47#[derive(Debug, Clone)]
48pub struct Config {
49    /// Base URL of the device (e.g., "http://192.168.8.1")
50    pub base_url: Url,
51    /// Request timeout for HTTP operations
52    pub timeout: Duration,
53    /// Maximum number of retry attempts for failed requests
54    pub max_retries: usize,
55    /// Initial delay before first retry
56    pub retry_delay: Duration,
57    /// Maximum delay between retries (for exponential backoff)
58    pub max_retry_delay: Duration,
59    /// User agent string sent with requests
60    pub user_agent: String,
61}
62
63impl Default for Config {
64    fn default() -> Self {
65        Self {
66            base_url: Url::parse("http://192.168.8.1").unwrap(),
67            timeout: Duration::from_secs(30),
68            max_retries: 3,
69            retry_delay: Duration::from_millis(500),
70            max_retry_delay: Duration::from_secs(30),
71            user_agent: format!("huawei-dongle-api/{}", env!("CARGO_PKG_VERSION")),
72        }
73    }
74}
75
76impl Config {
77    /// Create a new config builder
78    pub fn builder() -> ConfigBuilder {
79        ConfigBuilder::default()
80    }
81
82    /// Create a config with default settings for the given URL
83    pub fn for_url<S: AsRef<str>>(url: S) -> Result<Self> {
84        Ok(Self {
85            base_url: Url::parse(url.as_ref())?,
86            ..Default::default()
87        })
88    }
89}
90
91/// Builder for Config
92#[derive(Debug, Default)]
93pub struct ConfigBuilder {
94    base_url: Option<String>,
95    timeout: Option<Duration>,
96    max_retries: Option<usize>,
97    retry_delay: Option<Duration>,
98    max_retry_delay: Option<Duration>,
99    user_agent: Option<String>,
100}
101
102impl ConfigBuilder {
103    pub fn base_url<S: Into<String>>(mut self, url: S) -> Self {
104        self.base_url = Some(url.into());
105        self
106    }
107
108    pub fn timeout(mut self, timeout: Duration) -> Self {
109        self.timeout = Some(timeout);
110        self
111    }
112
113    pub fn max_retries(mut self, max_retries: usize) -> Self {
114        self.max_retries = Some(max_retries);
115        self
116    }
117
118    pub fn retry_delay(mut self, delay: Duration) -> Self {
119        self.retry_delay = Some(delay);
120        self
121    }
122
123    pub fn max_retry_delay(mut self, delay: Duration) -> Self {
124        self.max_retry_delay = Some(delay);
125        self
126    }
127
128    pub fn user_agent<S: Into<String>>(mut self, user_agent: S) -> Self {
129        self.user_agent = Some(user_agent.into());
130        self
131    }
132
133    pub fn build(self) -> Result<Config> {
134        let default = Config::default();
135
136        let base_url = if let Some(url) = self.base_url {
137            Url::parse(&url).map_err(|e| Error::config(format!("Invalid base URL: {e}")))?
138        } else {
139            default.base_url
140        };
141
142        Ok(Config {
143            base_url,
144            timeout: self.timeout.unwrap_or(default.timeout),
145            max_retries: self.max_retries.unwrap_or(default.max_retries),
146            retry_delay: self.retry_delay.unwrap_or(default.retry_delay),
147            max_retry_delay: self.max_retry_delay.unwrap_or(default.max_retry_delay),
148            user_agent: self.user_agent.unwrap_or(default.user_agent),
149        })
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_default_config() {
159        let config = Config::default();
160        assert_eq!(config.base_url.as_str(), "http://192.168.8.1/");
161        assert_eq!(config.timeout, Duration::from_secs(30));
162        assert_eq!(config.max_retries, 3);
163    }
164
165    #[test]
166    fn test_config_builder() {
167        let config = Config::builder()
168            .base_url("http://192.168.62.1")
169            .timeout(Duration::from_secs(60))
170            .max_retries(5)
171            .build()
172            .unwrap();
173
174        assert_eq!(config.base_url.as_str(), "http://192.168.62.1/");
175        assert_eq!(config.timeout, Duration::from_secs(60));
176        assert_eq!(config.max_retries, 5);
177    }
178
179    #[test]
180    fn test_for_url() {
181        let config = Config::for_url("http://192.168.62.1").unwrap();
182        assert_eq!(config.base_url.as_str(), "http://192.168.62.1/");
183    }
184}