Skip to main content

ferrous_browser/
lib.rs

1#![deny(missing_docs)]
2
3//! # ferrous-browser
4//!
5//! Full browser automation in Rust. No Node.js. No compromises. Single binary.
6//!
7//! ferrous-browser is a native Rust library for Chrome DevTools Protocol (CDP)
8//! communication, enabling full browser automation without Node.js.
9//!
10//! ## Quick Start
11//!
12//! ```no_run
13//! use ferrous_browser::{Browser, BrowserConfig, WaitUntil};
14//!
15//! #[tokio::main]
16//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
17//!     // Launch Chrome with defaults (headless, 1280×720, 30 s timeout)
18//!     let browser = Browser::launch_chrome(None).await?;
19//!
20//!     // Create a new page/tab
21//!     let page = browser.new_page().await?;
22//!
23//!     // Navigate to a website
24//!     page.goto("https://example.com", WaitUntil::Load).await?;
25//!
26//!     // Get the HTML content
27//!     let html = page.content().await?;
28//!     println!("Page HTML: {}", html);
29//!
30//!     // Take a screenshot
31//!     let png_bytes = page.screenshot().await?;
32//!     std::fs::write("screenshot.png", png_bytes)?;
33//!
34//!     Ok(())
35//! }
36//! ```
37//!
38//! ## Features
39//!
40//! - **Zero Node.js dependency** — Pure Rust implementation using tokio
41//! - **Single binary deployment** — No external runtime required
42//! - **Async-first API** — Built on tokio for high performance
43//! - **Type-safe** — Rust's type system catches errors at compile time
44//! - **Direct CDP access** — Full control over Chrome's DevTools Protocol
45//! - **Locator API** — Playwright-style ergonomic element selectors
46//!
47//! ## Locator API
48//!
49//! ```no_run
50//! use ferrous_browser::{Browser, WaitUntil};
51//!
52//! #[tokio::main]
53//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
54//!     let browser = Browser::launch_chrome(None).await?;
55//!     let page = browser.new_page().await?;
56//!     page.goto("https://example.com", WaitUntil::Load).await?;
57//!
58//!     // Locator API
59//!     page.locator("button#submit").click().await?;
60//!     page.locator("input[name=q]").type_text("hello").await?;
61//!     page.locator(".result").wait_for().await?;
62//!
63//!     Ok(())
64//! }
65//! ```
66//!
67//! ## WaitUntil modes
68//!
69//! ```no_run
70//! use ferrous_browser::{Browser, WaitUntil};
71//!
72//! # #[tokio::main]
73//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
74//! let browser = Browser::launch_chrome(None).await?;
75//! let page = browser.new_page().await?;
76//!
77//! // DOM parsed, sub-resources may still load
78//! page.goto("https://example.com", WaitUntil::DomContentLoaded).await?;
79//!
80//! // All resources loaded (default)
81//! page.goto("https://example.com", WaitUntil::Load).await?;
82//!
83//! // No network requests for 500 ms (SPA-friendly)
84//! page.goto("https://example.com", WaitUntil::NetworkIdle).await?;
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! ## Architecture
90//!
91//! ferrous-browser is built on a layered architecture:
92//!
93//! - **CDPClient** — Low-level CDP message routing
94//! - **Connection** — WebSocket lifecycle management
95//! - **Browser** — High-level browser control API
96//! - **Page** — High-level page/tab automation API
97//! - **Locator** — Lazy element handle for ergonomic element interaction
98
99/// Chrome DevTools Protocol types and client
100pub mod cdp;
101
102/// WebSocket connection management
103pub mod connection;
104
105/// Error types
106pub mod error;
107
108/// Browser automation API
109pub mod browser;
110
111/// Page automation API
112pub mod page;
113
114/// HTTP Archive (HAR) capture
115pub mod har;
116
117pub use browser::{Browser, BrowserConfig};
118pub use error::{BrowserError, ResultExt};
119pub use har::HarCapture;
120pub use page::{Cookie, Locator, Page, WaitUntil};
121
122#[cfg(test)]
123mod tests {
124    use crate::error::{BrowserError, ResultExt};
125
126    #[test]
127    fn it_works() {
128        // Verify library loads correctly
129    }
130
131    #[test]
132    fn test_error_display_timeout() {
133        let e = BrowserError::timeout("waiting for selector '.submit-btn'", 30);
134        assert_eq!(
135            e.to_string(),
136            "Timed out waiting for selector '.submit-btn' after 30s"
137        );
138    }
139
140    #[test]
141    fn test_error_display_navigation_failed() {
142        let e = BrowserError::navigation_failed("https://x.com", "net::ERR_NAME_NOT_RESOLVED");
143        assert_eq!(
144            e.to_string(),
145            "Navigation to 'https://x.com' failed: net::ERR_NAME_NOT_RESOLVED"
146        );
147    }
148
149    #[test]
150    fn test_result_context() {
151        let result: crate::error::Result<()> = Err(BrowserError::timeout("connecting", 5));
152        let with_ctx = result.context("Browser::launch_chrome");
153        assert!(with_ctx.is_err());
154        let msg = with_ctx.unwrap_err().to_string();
155        assert!(msg.contains("Browser::launch_chrome"));
156    }
157}