asknothingx2_util/api/
setup.rs

1use std::{
2    sync::{Arc, OnceLock, RwLock},
3    time::Duration,
4};
5
6use reqwest::Client;
7use tracing::{debug, info, warn};
8
9use super::{error::ConfigError, AppType, Config};
10
11static GLOBAL_CLIENT: OnceLock<Client> = OnceLock::new();
12static GLOBAL_CONFIG: OnceLock<Arc<RwLock<Config>>> = OnceLock::new();
13static GLOBAL_APP_TYPE: OnceLock<AppType> = OnceLock::new();
14
15pub fn get_global_client_or_default() -> &'static Client {
16    GLOBAL_CLIENT.get_or_init(|| {
17        warn!(
18            client_type = "default_web_app",
19            suggestion = "setup::web_apps()",
20            "HTTP client auto-initialized with default configuration"
21        );
22
23        Config::for_web_apps()
24            .build_client()
25            .expect("Failed to create default HTTP client")
26    })
27}
28
29fn initialize_global_client(config: Config) -> Result<(), ConfigError> {
30    let client = config.build_client()?;
31    let app_type = config.app_type;
32
33    GLOBAL_CLIENT
34        .set(client)
35        .map_err(|_| ConfigError::AlreadyConfigured {
36            message: "HTTP client has already been configured".to_string(),
37        })?;
38
39    GLOBAL_APP_TYPE
40        .set(config.app_type)
41        .map_err(|_| ConfigError::AlreadyConfigured {
42            message: "App type has already been set".to_string(),
43        })?;
44
45    let config_arc = Arc::new(RwLock::new(config));
46    GLOBAL_CONFIG
47        .set(config_arc)
48        .map_err(|_| ConfigError::AlreadyConfigured {
49            message: "Configuration has already been set".to_string(),
50        })?;
51
52    info!("HTTP client initialized for app type: {:?}", app_type);
53    Ok(())
54}
55
56fn detect_app_type_from_env() -> AppType {
57    match std::env::var("APP_TYPE")
58        .unwrap_or_else(|_| "web".to_string())
59        .to_lowercase()
60        .as_str()
61    {
62        "cli" | "command" | "script" => AppType::Cli,
63        "web" | "api" | "service" => AppType::Web,
64        "production" | "prod" | "enterprise" => AppType::Production,
65        "development" | "dev" | "test" | "testing" => AppType::Development,
66        "gateway" | "proxy" | "loadbalancer" => AppType::Gateway,
67        "scraping" | "crawler" | "scraper" => AppType::Scraping,
68        _ => AppType::Web,
69    }
70}
71
72fn apply_env_overrides(config: &mut Config) {
73    if let Ok(timeout) = std::env::var("HTTP_TIMEOUT_SECONDS") {
74        if let Ok(seconds) = timeout.parse::<u64>() {
75            config.request_timeout = Duration::from_secs(seconds);
76        }
77    }
78
79    if let Ok(proxy) = std::env::var("HTTP_PROXY") {
80        debug!("Applied HTTP proxy override: {}", proxy);
81        config.proxy_url = Some(proxy);
82    }
83
84    if let Ok(user_agent) = std::env::var("HTTP_USER_AGENT") {
85        debug!("Applied user agent override: {}", user_agent);
86        config.user_agent = user_agent;
87    }
88}
89
90pub fn is_configured() -> bool {
91    GLOBAL_CLIENT.get().is_some()
92}
93
94pub fn current_app_type() -> Option<AppType> {
95    GLOBAL_APP_TYPE.get().copied()
96}
97
98pub fn current_configuration() -> Option<Config> {
99    Some(GLOBAL_CONFIG.get()?.read().ok()?.clone())
100}
101
102pub fn update_global_config<F>(updater: F) -> Result<(), ConfigError>
103where
104    F: FnOnce(&mut Config),
105{
106    let config_arc = GLOBAL_CONFIG.get().ok_or(ConfigError::NotInitialized)?;
107    let mut config = config_arc
108        .write()
109        .map_err(|_| ConfigError::ConfigurationLocked)?;
110    updater(&mut config);
111    debug!("Configuration updated successfully");
112    Ok(())
113}
114
115pub fn app_type(app_type: AppType) -> Result<(), ConfigError> {
116    match app_type {
117        AppType::Cli => cli_tools(),
118        AppType::Web => web_apps(),
119        AppType::Production => production(),
120        AppType::Development => development(),
121        AppType::Gateway => api_gateway(),
122        AppType::Scraping => web_scraping(),
123    }
124}
125
126pub fn cli_tools() -> Result<(), ConfigError> {
127    let config = Config::for_cli_tools();
128    initialize_global_client(config)
129}
130
131pub fn web_apps() -> Result<(), ConfigError> {
132    let config = Config::for_web_apps();
133    initialize_global_client(config)
134}
135
136pub fn production() -> Result<(), ConfigError> {
137    let config = Config::for_production();
138    initialize_global_client(config)
139}
140
141pub fn development() -> Result<(), ConfigError> {
142    let config = Config::for_development();
143    initialize_global_client(config)
144}
145
146pub fn api_gateway() -> Result<(), ConfigError> {
147    let config = Config::for_api_gateway();
148    initialize_global_client(config)
149}
150
151pub fn web_scraping() -> Result<(), ConfigError> {
152    let config = Config::for_web_scraping();
153    initialize_global_client(config)
154}
155
156pub fn automatically() -> Result<(), ConfigError> {
157    let app_type = detect_app_type_from_env();
158
159    let mut config = match app_type {
160        AppType::Cli => Config::for_cli_tools(),
161        AppType::Web => Config::for_web_apps(),
162        AppType::Production => Config::for_production(),
163        AppType::Development => Config::for_development(),
164        AppType::Gateway => Config::for_api_gateway(),
165        AppType::Scraping => Config::for_web_scraping(),
166    };
167
168    apply_env_overrides(&mut config);
169    initialize_global_client(config)
170}