1use crate::server_middleware::BoxMiddlewareFuture;
2use crate::{
3 Extensions, Metadata, MetadataEntry, MetadataFlags, MetadataValue, MethodDescriptor, MethodId,
4 RequestCall, VoxError,
5};
6
7#[derive(Clone, Copy, Debug)]
9pub struct ClientContext<'a> {
10 method: Option<&'static MethodDescriptor>,
11 method_id: MethodId,
12 extensions: &'a Extensions,
13}
14
15impl<'a> ClientContext<'a> {
16 pub fn new(
17 method: Option<&'static MethodDescriptor>,
18 method_id: MethodId,
19 extensions: &'a Extensions,
20 ) -> Self {
21 Self {
22 method,
23 method_id,
24 extensions,
25 }
26 }
27
28 pub fn method(&self) -> Option<&'static MethodDescriptor> {
29 self.method
30 }
31
32 pub fn method_id(&self) -> MethodId {
33 self.method_id
34 }
35
36 pub fn extensions(&self) -> &'a Extensions {
37 self.extensions
38 }
39}
40
41pub struct ClientRequest<'call, 'state> {
46 call: &'state mut RequestCall<'call>,
47 owned_metadata: &'state mut OwnedMetadata,
48}
49
50impl<'call, 'state> ClientRequest<'call, 'state> {
51 pub fn new(
52 call: &'state mut RequestCall<'call>,
53 owned_metadata: &'state mut OwnedMetadata,
54 ) -> Self {
55 Self {
56 call,
57 owned_metadata,
58 }
59 }
60
61 pub fn call(&self) -> &RequestCall<'call> {
62 self.call
63 }
64
65 pub fn metadata(&self) -> &[MetadataEntry<'call>] {
66 &self.call.metadata
67 }
68
69 pub fn metadata_mut(&mut self) -> &mut Metadata<'call> {
70 &mut self.call.metadata
71 }
72
73 pub fn push_string_metadata(
74 &mut self,
75 key: &'static str,
76 value: impl Into<String>,
77 flags: MetadataFlags,
78 ) {
79 self.owned_metadata
80 .strings
81 .push(value.into().into_boxed_str());
82 let stored = self.owned_metadata.strings.last().unwrap();
83 let value: &'call str = unsafe { &*((&**stored) as *const str) };
87 self.call.metadata.push(MetadataEntry {
88 key: key.into(),
89 value: MetadataValue::String(value.into()),
90 flags,
91 });
92 }
93
94 pub fn push_bytes_metadata(
95 &mut self,
96 key: &'static str,
97 value: impl Into<Vec<u8>>,
98 flags: MetadataFlags,
99 ) {
100 self.owned_metadata
101 .bytes
102 .push(value.into().into_boxed_slice());
103 let stored = self.owned_metadata.bytes.last().unwrap();
104 let value: &'call [u8] = unsafe { &*((&**stored) as *const [u8]) };
106 self.call.metadata.push(MetadataEntry {
107 key: key.into(),
108 value: MetadataValue::Bytes(value.into()),
109 flags,
110 });
111 }
112
113 pub fn push_u64_metadata(&mut self, key: &'static str, value: u64, flags: MetadataFlags) {
114 self.call.metadata.push(MetadataEntry {
115 key: key.into(),
116 value: MetadataValue::U64(value),
117 flags,
118 });
119 }
120}
121
122#[derive(Default)]
123pub struct OwnedMetadata {
124 strings: Vec<Box<str>>,
125 bytes: Vec<Box<[u8]>>,
126}
127
128#[derive(Clone, Copy)]
129pub enum ClientCallOutcome<'a> {
130 Response,
131 Error(&'a VoxError),
132}
133
134impl ClientCallOutcome<'_> {
135 pub fn is_ok(self) -> bool {
136 matches!(self, Self::Response)
137 }
138}
139
140pub trait ClientMiddleware: Send + Sync + 'static {
141 fn pre<'a, 'call>(
142 &'a self,
143 _context: &'a ClientContext<'a>,
144 _request: &'a mut ClientRequest<'call, 'a>,
145 ) -> BoxMiddlewareFuture<'a> {
146 Box::pin(async {})
147 }
148
149 fn post<'a>(
150 &'a self,
151 _context: &'a ClientContext<'a>,
152 _outcome: ClientCallOutcome<'a>,
153 ) -> BoxMiddlewareFuture<'a> {
154 Box::pin(async {})
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use crate::Payload;
161
162 use super::{ClientRequest, MetadataFlags, MethodId, OwnedMetadata, RequestCall};
163
164 #[test]
165 fn client_request_can_add_owned_metadata() {
166 let mut call = RequestCall {
167 method_id: MethodId(1),
168 metadata: vec![],
169 args: Payload::PostcardBytes(&[]),
170 schemas: Default::default(),
171 };
172 let mut owned = OwnedMetadata::default();
173 let mut request = ClientRequest::new(&mut call, &mut owned);
174 request.push_string_metadata("x-test", "value".to_string(), MetadataFlags::NONE);
175 request.push_bytes_metadata("x-bytes", vec![1, 2, 3], MetadataFlags::NONE);
176 request.push_u64_metadata("x-num", 7, MetadataFlags::NONE);
177
178 assert_eq!(request.metadata().len(), 3);
179 assert!(matches!(
180 &request.metadata()[0].value,
181 crate::MetadataValue::String(s) if s == "value"
182 ));
183 assert!(matches!(
184 &request.metadata()[1].value,
185 crate::MetadataValue::Bytes(bytes) if bytes.as_ref() == [1, 2, 3]
186 ));
187 assert!(matches!(
188 request.metadata()[2].value,
189 crate::MetadataValue::U64(7)
190 ));
191 }
192}