Skip to main content

cratestack_axum/envelope_layer/
policy_request.rs

1//! [`PolicyRequest`]: what an [`super::EnvelopePolicy`] sees of a request.
2
3use http::Method;
4
5/// The op a request addresses, as an [`super::EnvelopePolicy`] sees it.
6///
7/// Deliberately **no headers** and no body (API-review decision,
8/// 2026-09-26): a policy that could read a header could be talked into a
9/// weaker mode by whoever writes that header. What it gets is the op:
10///
11/// - REST: the route template the schema declares (`/widgets/{id}`).
12/// - RPC unary: the op id (`procedure.ping`).
13/// - RPC subscription: the **bare** op id (`model.Widget.subscribe`), with
14///   [`is_subscription`](Self::is_subscription) set, so a policy keyed on
15///   op ids covers subscriptions without knowing their route (decision B1).
16/// - RPC batch: asked once for `batch` itself, and once per frame with the
17///   frame's op id and [`is_batch_frame`](Self::is_batch_frame) set; the
18///   call runs under the strictest answer (decision B1).
19#[derive(Debug, Clone, Copy)]
20pub struct PolicyRequest<'a> {
21    method: &'a Method,
22    op: &'a str,
23    subscription: bool,
24    batch_frame: bool,
25}
26
27impl<'a> PolicyRequest<'a> {
28    /// A request for `op`, as the layer describes it. Public so a policy
29    /// can be unit-tested; the layer builds its own.
30    pub fn new(method: &'a Method, op: &'a str) -> Self {
31        Self {
32            method,
33            op,
34            subscription: false,
35            batch_frame: false,
36        }
37    }
38
39    /// The same request, marked as a subscription.
40    pub fn subscription(self) -> Self {
41        Self {
42            subscription: true,
43            ..self
44        }
45    }
46
47    /// The same request, marked as one frame of a `/rpc/batch` call.
48    pub fn batch_frame(self) -> Self {
49        Self {
50            batch_frame: true,
51            ..self
52        }
53    }
54
55    /// The HTTP method (`POST` for every batch frame).
56    pub fn method(&self) -> &'a Method {
57        self.method
58    }
59
60    /// The op: a REST route template or an RPC op id (see the type docs).
61    pub fn op(&self) -> &'a str {
62        self.op
63    }
64
65    /// An RPC subscription (`/rpc/subscribe/{op_id}`).
66    pub fn is_subscription(&self) -> bool {
67        self.subscription
68    }
69
70    /// One frame of a `/rpc/batch` call.
71    pub fn is_batch_frame(&self) -> bool {
72        self.batch_frame
73    }
74}