lmjtfy.git / apps / lmjtfy / src / object.rs
1//! How the Worker, or one Durable Object, talks to a Durable Object: a JSON
2//! message posted to it, and a JSON message back.
3
4use serde::Serialize;
5use serde::de::DeserializeOwned;
6use worker::{Env, Method, Request, RequestInit};
7
8/// Posts `ask` to the object called `name` behind `binding`. `what` names the
9/// object in an error.
10pub async fn tell<A: Serialize, T: DeserializeOwned>(
11    env: &Env,
12    binding: &str,
13    name: &str,
14    what: &str,
15    ask: &A,
16) -> Result<T, String> {
17    let unreachable = |e: worker::Error| format!("{what}: {e}");
18    let namespace = env.durable_object(binding).map_err(unreachable)?;
19    let stub = namespace.id_from_name(name).and_then(|id| id.get_stub()).map_err(unreachable)?;
20    let body = serde_json::to_string(ask).map_err(|e| format!("{what}: {e}"))?;
21    let mut init = RequestInit::new();
22    init.with_method(Method::Post).with_body(Some(body.into()));
23    // The host is never resolved: a stub's fetch goes to its object.
24    let request = Request::new_with_init("https://object/", &init).map_err(unreachable)?;
25    let mut response = stub.fetch_with_request(request).await.map_err(unreachable)?;
26    let text = response.text().await.map_err(unreachable)?;
27    serde_json::from_str(&text).map_err(|e| format!("{what}: {e}"))
28}