request_shadow/backend.rs
1//! The async backend abstraction.
2
3use std::collections::BTreeMap;
4
5use async_trait::async_trait;
6use bytes::Bytes;
7
8use crate::error::ShadowError;
9
10/// What both legs of a shadow call return.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct ResponseRecord {
13 /// Whether the call completed normally. Backends that handle their own
14 /// errors should leave this `true` and put any error payload in `body`.
15 pub ok: bool,
16 /// HTTP-ish status code (or 0 for non-HTTP calls). Diffed unless the
17 /// caller adds it to [`crate::config::IgnoreField::Status`].
18 pub status: u16,
19 /// Sorted-by-key headers map. Diffed unless [`crate::config::IgnoreField::Headers`].
20 pub headers: BTreeMap<String, String>,
21 /// Response body, opaque.
22 pub body: Bytes,
23}
24
25impl ResponseRecord {
26 /// Construct a success record from a body — handy in tests.
27 pub fn ok(body: Vec<u8>) -> Self {
28 Self {
29 ok: true,
30 status: 200,
31 headers: BTreeMap::new(),
32 body: Bytes::from(body),
33 }
34 }
35
36 /// Construct a failure record.
37 pub fn err(status: u16, body: Vec<u8>) -> Self {
38 Self {
39 ok: false,
40 status,
41 headers: BTreeMap::new(),
42 body: Bytes::from(body),
43 }
44 }
45
46 /// Convenience: set a header. Returns `self` for chaining.
47 #[must_use]
48 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
49 self.headers.insert(key.into(), value.into());
50 self
51 }
52}
53
54/// The async surface the shadower drives. Implement once per transport.
55#[async_trait]
56pub trait Backend: Send + Sync {
57 /// Issue a call. The input is opaque so this trait works for HTTP bodies,
58 /// gRPC bytes, raw JSON, MessagePack — anything that fits in a `&[u8]`.
59 async fn call(&self, input: &[u8]) -> Result<ResponseRecord, ShadowError>;
60}