@@ -14,217 +14,107 @@ It exposes two areas:
1414The examples below mirror common usage: creating a
1515manifest, sending/receiving JSON messages, and running an async loop.
1616
17- ---
17+ ## Encode a JSON message into a native-messaging frame
1818
19- ## Quick start
20-
21- Add deps:
22-
23- ```toml
24- [dependencies]
25- native_messaging = { path = "." } # this crate, if you're developing locally
26- tokio = { version = "1", features = ["full"] }
27- serde = { version = "1", features = ["derive"] }
28- serde_json = "1"
29- ```
30-
31- ---
32-
33- ## The wire format (tl;dr)
34-
35- ```text
36- [0..4) : 32-bit length (native-endian)
37- [4..N) : UTF-8 JSON payload of that length
38- ```
39-
40- Browsers spawn your program and connect **stdin/stdout**; you read frames from
41- stdin and write frames to stdout.
42-
43- ---
44-
45- ## Messaging examples
46-
47- ### Read a single message
48-
49- ```no_run
50- use native_messaging::host::get_message;
51-
52- #[tokio::main]
53- async fn main() {
54- match get_message().await {
55- Ok(json) => println!("Received: {json}"),
56- Err(e) => eprintln!("Error receiving message: {e}"),
57- }
58- }
59- ```
60-
61- ### Send a single message
62-
63- ```no_run
64- use native_messaging::host::send_message;
65- use serde::Serialize;
66-
67- #[derive(Serialize)]
68- struct MyMessage { content: String }
69-
70- #[tokio::main]
71- async fn main() -> std::io::Result<()> {
72- let msg = MyMessage { content: "Hello, world!".into() };
73- send_message(&msg).await
74- }
75- ```
76-
77- ### Encode a message to a native-messaging frame
78-
79- ```no_run
19+ ```rust
8020use native_messaging::host::encode_message;
8121use serde::Serialize;
8222
8323#[derive(Serialize)]
84- struct Msg { hello: String }
85-
86- fn main() {
87- let frame = encode_message(&Msg { hello: "world".into() })
88- .expect("serialize");
89- assert!(frame.len() >= 4);
24+ struct Msg {
25+ hello: &'static str,
9026}
91- ```
92-
93- ### Run an async event loop
9427
95- ```no_run
96- use native_messaging::host::{event_loop, send_message};
97-
98- #[tokio::main]
99- async fn main() {
100- event_loop(|raw: String| async move {
101- // Echo the raw JSON back to the browser
102- send_message(&raw).await
103- }).await;
104- }
28+ // Encode to a 4-byte-length-prefixed frame:
29+ let frame = encode_message(&Msg { hello: "world" }).unwrap();
30+ assert!(frame.len() >= 4);
10531```
10632
107- ### Structured messages (enums )
33+ ## Read a message and reply on stdout (async )
10834
10935```no_run
110- use native_messaging::host::{event_loop , send_message};
36+ use native_messaging::host::{get_message , send_message};
11137use serde::{Deserialize, Serialize};
11238
11339#[derive(Deserialize)]
114- #[serde(tag="type", rename_all="snake_case")]
115- enum In {
116- Ping { id: u64, payload: String },
117- }
40+ struct In { ping: String }
11841
11942#[derive(Serialize)]
120- struct Out { id: u64, echo: String, ok: bool }
121-
122- #[tokio::main]
123- async fn main() {
124- event_loop(|raw: String| async move {
125- match serde_json::from_str::<In>(&raw) {
126- Ok(In::Ping { id, payload }) => {
127- send_message(&Out { id, echo: payload, ok: true }).await
128- }
129- Err(e) => {
130- eprintln!("bad json: {e}");
131- Ok(())
132- }
133- }
134- }).await;
135- }
136- ```
137-
138- ---
43+ struct Out { pong: String }
13944
140- ## Host manifest management
45+ // Create a minimal Tokio runtime just for the example:
46+ let rt = tokio::runtime::Runtime::new().unwrap();
47+ rt.block_on(async {
48+ // Your API returns a raw String
49+ let raw = get_message().await.unwrap();
14150
142- > On macOS/Linux the manifest `path` **must be absolute** (use your built
143- > binary path). Chrome/Edge use `allowed_origins`; Firefox uses
144- > `allowed_extensions`. This crate abstracts the placement logic per browser.
51+ // Parse to your input type
52+ let incoming: In = serde_json::from_str(&raw).unwrap();
14553
146- ### Install for multiple browsers
147-
148- ```ignore
149- // This is marked `ignore` so rustdoc won't try to compile it, because the exact
150- // signature of `install` (browsers/enums/scope) depends on your crate version.
151- // Use this as a template and adjust types to your API.
152-
153- use std::path::Path;
154- use native_messaging::install::manifest::{install, Browser, Scope};
155-
156- fn main() -> std::io::Result<()> {
157- let path = Path::new("/absolute/path/to/target/release/host-binary");
158- let allowed_origins: Vec<String> = vec![
159- "chrome-extension://your_ext_id/".to_string(),
160- "chrome-extension://another_id/".to_string(),
161- ];
162-
163- let browsers = &[Browser::Chrome, Browser::Firefox];
164-
165- // Adjust arguments to match your `install` signature exactly.
166- install("com.example.host", "Example Host", path, &allowed_origins, browsers, Scope::User)
167- }
54+ // Build and send a reply
55+ let reply = Out { pong: incoming.ping };
56+ send_message(&reply).await.unwrap();
57+ });
16858```
16959
170- ### Verify / Remove
60+ ## Run the event loop
17161
172- ```ignore
173- // Marked `ignore` to avoid signature drift across versions.
174- // Adapt to your actual function signatures.
175-
176- use native_messaging::install::manifest::{verify, remove, Browser, Scope};
177-
178- fn main() -> std::io::Result<()> {
179- // Some versions take only `name`, others also take browsers/scope.
180- if verify("com.example.host")? {
181- // Adjust remove signature as needed (browsers/scope).
182- remove("com.example.host", &[Browser::Firefox], Scope::User)?;
183- }
184- Ok(())
185- }
186- ```
187-
188- ---
189-
190- ## Extension-side (Chrome/Edge MV3)
62+ ```no_run
63+ use native_messaging::host::{event_loop, send_message};
64+ use serde::Serialize;
65+ use std::io;
19166
192- ```javascript
193- // background.js
194- const port = chrome.runtime.connectNative("com.example.host");
195- port.onMessage.addListener(msg => console.log("host:", msg));
196- port.postMessage({ type: "ping", payload: "hello", id: 1 });
67+ #[derive(Serialize)]
68+ struct Out { pong: String }
69+
70+ // The event loop receives raw String messages and expects the handler future to
71+ // resolve to io::Result<()>.
72+ let rt = tokio::runtime::Runtime::new().unwrap();
73+ rt.block_on(async {
74+ event_loop(|msg: String| async move {
75+ // In real code, you'd parse `msg` into a structured type first.
76+ let reply = Out { pong: msg };
77+ send_message(&reply).await?;
78+ Ok::<(), io::Error>(())
79+ })
80+ .await;
81+ });
19782```
19883
199- Ensure your host manifest lists the extension (origin for Chrome/Edge or
200- extension ID for Firefox).
201-
202- ---
203-
204- ## Troubleshooting
84+ ## Create and install a manifest
20585
206- - **No response?** Make sure you write to **stdout** (native messaging uses stdio).
207- - **Absolute path**: on macOS/Linux the `path` in the manifest must be **absolute**.
208- - Use `verify("com.example.host")` (or your version’s signature) to check install status.
209-
210- ---
211-
212- ## About these docs
213-
214- This file provides a comprehensive crate-level guide (what you see now) and
215- then **inlines** the function-level items from your implementation using
216- `#[doc(inline)] pub use …` so everything appears in one place in `cargo doc`.
217-
218- Run:
86+ ```no_run
87+ use std::path::Path;
88+ use native_messaging::install::manifest::{install, Browser, Scope};
21989
220- ```sh
221- cargo doc --no-deps --open
90+ let name = "com.example.host";
91+ let description = "Example native messaging host";
92+ let path = Path::new("/absolute/path/to/target/release/host-binary");
93+ let allowed_origins: Vec<String> = vec![
94+ "chrome-extension://your_ext_id/".to_string(),
95+ "chrome-extension://another_id/".to_string(),
96+ ];
97+ // List of Chrome/Firefox extension IDs that can talk to your host (if applicable)
98+ let allowed_extensions: Vec<String> = vec![];
99+
100+ let browsers = &[Browser::Chrome, Browser::Firefox];
101+
102+ // Adjust arguments to your needs; Scope::User for per-user install.
103+ // NOTE: This matches the signature hinted by the compiler:
104+ // install(name: &str, description: &str, path: &Path, allowed_origins: &[String],
105+ // allowed_extensions: &[String], browsers: &[Browser], scope: Scope)
106+ install(
107+ name,
108+ description,
109+ path,
110+ &allowed_origins,
111+ &allowed_extensions,
112+ browsers,
113+ Scope::User
114+ ).unwrap();
222115```
223116"# ]
224117
225- // ========= Re-export the public API so it appears inline in these docs =========
226-
227- // Keep using on-disk modules (e.g., src/host.rs, src/install/manifest.rs)
228118pub mod host;
229119pub mod install;
230120
0 commit comments