cufflink_native/lib.rs
1//! Native Cufflink services: extensions written in Rust that WASM services
2//! call through `ext_call`.
3//!
4//! A native service is a binary whose `main` names the service and the
5//! extensions it provides, each with the services allowed to call it:
6//!
7//! ```no_run
8//! use cufflink_native::prelude::*;
9//!
10//! struct GoogleToken;
11//!
12//! impl Extension for GoogleToken {
13//! fn call(&self, ctx: &CallContext, payload: Value) -> Result<Value> {
14//! let scope = payload["scope"].as_str().ok_or("scope is required")?;
15//! Ok(json!({ "token": scope, "tenant": ctx.tenant }))
16//! }
17//! }
18//!
19//! fn main() -> std::process::ExitCode {
20//! cufflink_native::service("ext-google")
21//! .extension("google.token", GoogleToken, ["reports-service"])
22//! .run()
23//! }
24//! ```
25//!
26//! [`Service::run`] prints the service's manifest under `--emit-manifest`
27//! (what `cufflink deploy` reads) and otherwise serves the Unix socket the
28//! extension runner starts it with. [`Service::run_nats`] serves the same
29//! extensions straight from NATS, as a provider the project runs itself.
30//!
31//! Each call runs on the blocking pool with the caller's [`CallContext`]
32//! and is given up at its [deadline](CallDeadline::deadline). Besides the
33//! codes of an extension's own [`ExtError`]s, a call fails with
34//! `not_found` (no such extension), `timeout` (the deadline passed),
35//! `panic` (the extension panicked), `invalid` (a malformed call, or on
36//! NATS one whose body does not match its subject), `unsupported_protocol`
37//! (a caller speaking another protocol), `unavailable` (the service is
38//! shutting down) or `too_large` (a reply over the NATS server's maximum
39//! payload). On NATS, a call whose deadline has passed gets no reply.
40
41mod deadline;
42mod dispatch;
43mod error;
44mod nats;
45mod service;
46mod socket;
47
48pub use cufflink_nats::NatsAuth;
49pub use cufflink_types::ext::CallContext;
50pub use deadline::CallDeadline;
51pub use error::{ExtError, Result};
52pub use nats::{NatsProvider, NatsServer};
53pub use service::{service, Service};
54pub use socket::SocketServer;
55
56use serde_json::Value;
57
58/// An extension: what answers the calls made to one name.
59///
60/// `call` runs on the blocking pool, so it may block; closures and
61/// functions of the same signature are extensions too.
62pub trait Extension: Send + Sync + 'static {
63 fn call(&self, ctx: &CallContext, payload: Value) -> Result<Value>;
64}
65
66impl<F> Extension for F
67where
68 F: Fn(&CallContext, Value) -> Result<Value> + Send + Sync + 'static,
69{
70 fn call(&self, ctx: &CallContext, payload: Value) -> Result<Value> {
71 self(ctx, payload)
72 }
73}
74
75/// What an extension needs in scope.
76pub mod prelude {
77 pub use crate::{CallContext, CallDeadline, ExtError, Extension, Result};
78 pub use serde_json::{json, Value};
79}