A high-performance, zero-allocation, fully compile-safe Rust representation of the Chrome DevTools JavaScript Protocol (js_protocol), generated directly from the official protocol definitions.
Most auto-generated CDP crates output raw, unidiomatic APIs with substantial runtime allocation overhead. This library is designed from the ground up to solve these issues:
- All generated struct fields, getters, and builder setter methods are translated from the protocol's raw
camelCaseto standard Rustsnake_case(e.g.,executionContextIdbecomesexecution_context_id, andobjectGroupbecomesobject_group). - Standard
#[serde(rename = "...")]attributes ensure the serialized JSON wire protocol matches the exact formats required by Chrome.
- Utilizes
Cow<'a, str>instead of allocating heap memory (String) for string properties. - String arguments in builders use
impl Into<Cow<'a, str>>, allowing you to pass static string literals (&str) or owned strings without unnecessary heap allocations.
- The builder pattern differentiates between required and optional parameters. Required parameters are passed directly as arguments to the
builder(...)function, guaranteeing protocol compliance at compile time:// `expression` is required (passed to builder), `silent` is optional (chained) let params = EvaluateParams::builder("1 + 1") .silent(true) .build();
- Contains zero bloated dependencies (depends only on
serdeandserde_json). - Does not include a WebSocket client or force a specific async runtime (like
tokio). This keeps the package extremely lightweight, compile-fast, and compatible with any async runtime or network stack.
Add this to your Cargo.toml:
[dependencies]
js-protocol = { version = "0.1.3", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"use js_protocol::runtime::EvaluateParams;
fn main() {
// 1. Build the command parameters
let params = EvaluateParams::builder("console.log('Hello from Rust!')")
.silent(true)
.build();
// 2. Read-only getters
println!("Expression to run: {}", params.expression());
// 3. Serialize to wire protocol payload (skips unset Option fields)
let payload = serde_json::to_string(¶ms).unwrap();
println!("Payload: {}", payload);
// Output: {"expression":"console.log('Hello from Rust!')","silent":true}
}Every parameter struct implements crate::CdpCommand<'a> which binds it to its command method and its corresponding response (Returns) type:
use js_protocol::runtime::{EvaluateParams, EvaluateReturns};
use js_protocol::CdpCommand;
fn evaluate_js() {
let params = EvaluateParams::builder("1 + 2").build();
// The trait binds this command to its method name and response type:
assert_eq!(EvaluateParams::METHOD, "Runtime.evaluate");
// In your network client:
// let response_json = websocket.send_command(EvaluateParams::METHOD, ¶ms).await;
// let response: EvaluateReturns = serde_json::from_str(&response_json).unwrap();
}The code is dynamically compiled using a custom Python script that performs advanced schema analysis:
- Fixed-Point Lifetime Propagation Pass: The generator performs iterative analysis over the CDP types to detect circular type references, nesting, and dependency hierarchies. It automatically determines which types must have a lifetime parameter (
<'a>) and wraps recursive structures insideBoxto prevent infinite-size compilation errors. - HTML/Markdown Escaping: Schema documentation from the Chrome DevTools Protocol contains raw markdown and HTML brackets. The generator cleans and escapes these brackets into valid Rustdoc format, keeping compilation entirely warning-free.
- Domain-Specific Feature Flags: Every CDP domain is represented by a Rust feature flag. You can optimize compile times by only compiling the domains your project needs:
# Compile only the runtime and debugger domains js-protocol = { version = "0.1.3", default-features = false, features = ["runtime", "debugger"] }
To regenerate the Rust modules from a local protocol file:
python scripts/generate_rust_code.py --name js-protocolDistributed under the MIT License. See LICENSE for more information.