1use active_win_pos_rs::get_active_window;
28use serde::{Deserialize, Serialize};
29
30pub mod browser_detection;
31pub mod error;
32pub mod url_extraction;
33
34pub mod platform;
35
36pub use error::BrowserInfoError;
37
38#[cfg(any(
39 all(feature = "devtools", target_os = "windows"),
40 all(doc, feature = "devtools")
41))]
42pub use platform::chrome_devtools::ChromeDevToolsExtractor;
43
44#[derive(Debug, Clone, Copy)]
49pub enum ExtractionMethod {
50 Auto,
52 DevTools,
54 PowerShell,
56}
57
58pub struct BrowserInfo {
60 pub url: String,
62 pub title: String,
63 pub browser_name: String,
64 pub browser_type: BrowserType,
65 pub version: Option<String>,
66 pub tabs_count: Option<u32>,
67 pub is_incognito: bool,
68 pub process_id: u64,
70 pub window_position: WindowPosition,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
76pub enum BrowserType {
77 Chrome,
78 Firefox,
79 Edge,
80 Safari,
81 Brave,
82 Opera,
83 Vivaldi,
84 Unknown(String),
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
89pub struct WindowPosition {
90 pub x: f64,
91 pub y: f64,
92 pub width: f64,
93 pub height: f64,
94}
95
96pub fn get_active_browser_info() -> Result<BrowserInfo, BrowserInfoError> {
119 if !is_browser_active() {
121 return Err(BrowserInfoError::NotABrowser);
122 }
123
124 let window = get_active_window().map_err(|_| BrowserInfoError::WindowNotFound)?;
126
127 let browser_type = browser_detection::classify_browser(&window)?;
129
130 let page = url_extraction::extract_page(&window, &browser_type)?;
132
133 let title = match page.title {
137 Some(title) => title,
138 None => {
139 if window.title.is_empty() {
140 println!(
141 "⚠️ No page title available: platform extraction gave none and the window title is empty"
142 );
143 }
144 window.title.clone()
145 }
146 };
147
148 let metadata = browser_detection::get_browser_metadata(&window, &browser_type)?;
150
151 Ok(BrowserInfo {
152 url: page.url,
153 title,
154 browser_name: window.app_name,
155 browser_type,
156 version: metadata.version,
157 tabs_count: metadata.tabs_count,
158 is_incognito: metadata.is_incognito,
159 process_id: window.process_id,
160 window_position: WindowPosition {
161 x: window.position.x,
162 y: window.position.y,
163 width: window.position.width,
164 height: window.position.height,
165 },
166 })
167}
168
169pub fn get_active_browser_url() -> Result<String, BrowserInfoError> {
171 if !is_browser_active() {
173 return Err(BrowserInfoError::NotABrowser);
174 }
175
176 let window = get_active_window().map_err(|_| BrowserInfoError::WindowNotFound)?;
177
178 let browser_type = browser_detection::classify_browser(&window)?;
179 url_extraction::extract_url(&window, &browser_type)
180}
181
182pub fn is_browser_active() -> bool {
184 if let Ok(window) = get_active_window() {
185 browser_detection::classify_browser(&window).is_ok()
186 } else {
187 false
188 }
189}
190
191pub fn get_browser_info_safe() -> Result<BrowserInfo, BrowserInfoError> {
193 get_active_browser_info()
194}
195
196#[cfg(any(
198 all(feature = "devtools", target_os = "windows"),
199 all(doc, feature = "devtools")
200))]
201pub async fn get_browser_info_detailed() -> Result<BrowserInfo, BrowserInfoError> {
202 ChromeDevToolsExtractor::extract_browser_info().await
203}
204
205#[cfg(any(
207 all(feature = "devtools", target_os = "windows"),
208 all(doc, feature = "devtools")
209))]
210pub async fn get_browser_info_fast() -> Result<BrowserInfo, BrowserInfoError> {
211 get_browser_info_detailed().await
212}
213
214pub async fn get_browser_info() -> Result<BrowserInfo, BrowserInfoError> {
216 match get_browser_info_safe() {
218 Ok(info) => {
219 println!("✅ Using PowerShell method (fastest)");
220 return Ok(info);
221 }
222 Err(e) => {
223 println!("⚠️ PowerShell failed: {e}, trying DevTools...");
224 }
225 }
226
227 #[cfg(all(feature = "devtools", target_os = "windows"))]
229 if ChromeDevToolsExtractor::is_available().await {
230 println!("🔄 Fallback to Chrome DevTools Protocol");
231 return ChromeDevToolsExtractor::extract_browser_info().await;
232 }
233
234 Err(BrowserInfoError::Other(
235 "All extraction methods failed".to_string(),
236 ))
237}
238
239pub async fn get_browser_info_with_method(
241 method: ExtractionMethod,
242) -> Result<BrowserInfo, BrowserInfoError> {
243 match method {
244 ExtractionMethod::Auto => get_browser_info().await,
245 #[cfg(any(
246 all(feature = "devtools", target_os = "windows"),
247 all(doc, feature = "devtools")
248 ))]
249 ExtractionMethod::DevTools => get_browser_info_detailed().await,
250 #[cfg(not(any(
251 all(feature = "devtools", target_os = "windows"),
252 all(doc, feature = "devtools")
253 )))]
254 ExtractionMethod::DevTools => Err(BrowserInfoError::Other(
255 "DevTools feature not available on this platform".to_string(),
256 )),
257 ExtractionMethod::PowerShell => get_browser_info_safe(),
258 }
259}