Skip to main content

frust_devtools_protocol/
lib.rs

1//! `frust-devtools-protocol` — the wire contract between an in-app Frust
2//! debug service and external tooling (`frust-drive`/`frust-tui`).
3//!
4//! This is the **one sanctioned crossing** of the tooling-isolation charter
5//! (`docs/ARCHITECTURE.md`'s Cross-Unit Layer Dependencies: "`frust-cli`,
6//! `frust-drive`, and `frust-tui` depend on NO framework crate"). Both sides
7//! of the devtools wire depend on this crate, and only this crate, to agree
8//! on message shapes — neither side depends on the other, and this crate
9//! stays a **leaf**: `serde` (derive) + `serde_json` only, no `tokio`, no
10//! framework crate, no other `frust-*` crate. Pulling it into the tooling
11//! side must never drag framework or async-runtime code along with it.
12//!
13//! # Framing
14//!
15//! One JSON-RPC 2.0 object per `\n`-terminated line — no `Content-Length`
16//! headers. [`encode_line`]/[`decode_line`] are the pure (no I/O)
17//! serialize/discriminate pair; [`Incoming`] tells the caller whether a
18//! decoded line was a [`Request`], [`Response`], or [`Notification`].
19//!
20//! # Discovery and auth
21//!
22//! A server announces its listening port — and the per-process token a client
23//! must present at `handshake` — with one printed line built from
24//! [`DISCOVERY_PREFIX`]. [`format_discovery_line`]/[`parse_discovery_line`]
25//! are the single formatter/parser pair the service and tooling share, and
26//! [`Discovery`] is what a parsed line yields. The token travels back to the
27//! server exactly once, in [`HandshakeParams`]; a connection that has not
28//! presented it is answered [`RpcError::UNAUTHORIZED`] for every other method.
29//!
30//! # Methods
31//!
32//! [`Method`] is the typed v1 method set; [`messages`]-derived re-exports
33//! below are each method's typed params/result/notification-payload struct.
34//! This crate is pure data + pure functions — no I/O, no async, no runtime
35//! state of its own.
36
37mod codec;
38mod discovery;
39mod messages;
40mod method;
41mod types;
42
43/// Whole-crate valve for `serde_json`, this crate's one public-API dependency.
44///
45/// [`Request::params`]/[`Response`]'s `result` are `serde_json::Value`, so a
46/// peer cannot build or read a message without that crate — and it must be the
47/// *same* version this crate speaks. Reaching through this valve
48/// (`frust_devtools_protocol::serde_json::to_value(..)`) instead of
49/// re-declaring the dependency keeps the pin in exactly one manifest, the same
50/// rule `frust::kurbo`/`frust::peniko` follow for app code
51/// (`docs/CODE_STANDARDS.md`'s State & Reactivity Conventions).
52pub use serde_json;
53
54pub use codec::{DecodeError, decode_line, encode_line};
55pub use discovery::{
56    DISCOVERY_PREFIX, Discovery, FAILURE_PREFIX, format_discovery_line, format_failure_line,
57    parse_discovery_line, parse_failure_line, redact_discovery_token,
58};
59pub use messages::{
60    AckResult, Capability, FrameStats, HandshakeInfo, HandshakeParams, InputScrollParams,
61    InputTapParams, InputTextParams, MetricsSnapshot, RectPx, ScreenshotResult, WidgetNode,
62    WidgetProps, WidgetPropsParams, WidgetTreeDump,
63};
64pub use method::Method;
65pub use types::{
66    Incoming, JSONRPC_VERSION, Notification, Request, Response, ResponseOutcome, RpcError,
67};
68
69/// The devtools wire protocol version this crate implements —
70/// [`HandshakeInfo::protocol_version`]'s value.
71pub const PROTOCOL_VERSION: u32 = 1;