1use super::DurableFuture;
2
3use crate::endpoint::ContextInternal;
4use crate::errors::TerminalError;
5use crate::serde::{Deserialize, Serialize};
6use futures::FutureExt;
7use futures::future::BoxFuture;
8use std::fmt;
9use std::future::{Future, IntoFuture};
10use std::marker::PhantomData;
11use std::time::Duration;
12
13#[derive(Debug, Clone)]
15pub enum RequestTarget {
16 Service {
17 name: String,
18 handler: String,
19 },
20 Object {
21 name: String,
22 key: String,
23 handler: String,
24 },
25 Workflow {
26 name: String,
27 key: String,
28 handler: String,
29 },
30}
31
32impl RequestTarget {
33 pub fn service(name: impl Into<String>, handler: impl Into<String>) -> Self {
34 Self::Service {
35 name: name.into(),
36 handler: handler.into(),
37 }
38 }
39
40 pub fn object(
41 name: impl Into<String>,
42 key: impl Into<String>,
43 handler: impl Into<String>,
44 ) -> Self {
45 Self::Object {
46 name: name.into(),
47 key: key.into(),
48 handler: handler.into(),
49 }
50 }
51
52 pub fn workflow(
53 name: impl Into<String>,
54 key: impl Into<String>,
55 handler: impl Into<String>,
56 ) -> Self {
57 Self::Workflow {
58 name: name.into(),
59 key: key.into(),
60 handler: handler.into(),
61 }
62 }
63}
64
65impl fmt::Display for RequestTarget {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 RequestTarget::Service { name, handler } => write!(f, "{name}/{handler}"),
69 RequestTarget::Object { name, key, handler } => write!(f, "{name}/{key}/{handler}"),
70 RequestTarget::Workflow { name, key, handler } => write!(f, "{name}/{key}/{handler}"),
71 }
72 }
73}
74
75pub struct Request<'a, Req, Res = ()> {
77 ctx: &'a ContextInternal,
78 request_target: RequestTarget,
79 idempotency_key: Option<String>,
80 scope: Option<String>,
81 limit_key: Option<String>,
82 headers: Vec<(String, String)>,
83 req: Req,
84 res: PhantomData<Res>,
85}
86
87impl<'a, Req, Res> Request<'a, Req, Res> {
88 pub(crate) fn new(ctx: &'a ContextInternal, request_target: RequestTarget, req: Req) -> Self {
89 Self {
90 ctx,
91 request_target,
92 idempotency_key: None,
93 scope: None,
94 limit_key: None,
95 headers: vec![],
96 req,
97 res: PhantomData,
98 }
99 }
100
101 pub fn header(mut self, key: String, value: String) -> Self {
102 self.headers.push((key, value));
103 self
104 }
105
106 pub fn idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
108 self.idempotency_key = Some(idempotency_key.into());
109 self
110 }
111
112 pub fn scope(mut self, scope: impl Into<String>) -> Self {
119 self.scope = Some(scope.into());
120 self
121 }
122
123 pub fn limit_key(mut self, limit_key: impl Into<String>) -> Self {
128 self.limit_key = Some(limit_key.into());
129 self
130 }
131
132 pub fn call(self) -> impl CallFuture<Response = Res> + Send
134 where
135 Req: Serialize + 'static,
136 Res: Deserialize + 'static,
137 {
138 self.ctx.call(
139 self.request_target,
140 self.idempotency_key,
141 self.scope,
142 self.limit_key,
143 self.headers,
144 self.req,
145 )
146 }
147
148 pub fn send(self) -> SendHandle
153 where
154 Req: Serialize + 'static,
155 {
156 self.ctx.send(
157 self.request_target,
158 self.idempotency_key,
159 self.scope,
160 self.limit_key,
161 self.headers,
162 self.req,
163 None,
164 )
165 }
166
167 pub fn send_after(self, delay: Duration) -> SendHandle
171 where
172 Req: Serialize + 'static,
173 {
174 self.ctx.send(
175 self.request_target,
176 self.idempotency_key,
177 self.scope,
178 self.limit_key,
179 self.headers,
180 self.req,
181 Some(delay),
182 )
183 }
184}
185
186pub struct InvocationHandle {
197 ctx: ContextInternal,
198 invocation_id: String,
199}
200
201impl InvocationHandle {
202 pub(crate) fn new(ctx: ContextInternal, invocation_id: String) -> Self {
203 Self { ctx, invocation_id }
204 }
205
206 pub fn invocation_id(&self) -> &str {
208 &self.invocation_id
209 }
210
211 pub fn cancel(&self) {
213 self.ctx.cancel_invocation(&self.invocation_id)
214 }
215
216 pub fn attach<T: Deserialize + 'static>(
218 &self,
219 ) -> impl DurableFuture<Output = Result<T, TerminalError>> + Send + use<T> {
220 self.ctx.attach_invocation(self.invocation_id.clone())
221 }
222
223 pub fn signal(&self, name: impl Into<String>) -> SignalHandle {
226 SignalHandle {
227 ctx: self.ctx.clone(),
228 invocation_id: self.invocation_id.clone(),
229 name: name.into(),
230 }
231 }
232}
233
234pub struct SignalHandle {
239 ctx: ContextInternal,
240 invocation_id: String,
241 name: String,
242}
243
244impl SignalHandle {
245 pub fn resolve<T: Serialize + 'static>(self, value: T) {
247 self.ctx
248 .resolve_signal(&self.invocation_id, &self.name, value)
249 }
250
251 pub fn reject(self, failure: TerminalError) {
253 self.ctx
254 .reject_signal(&self.invocation_id, &self.name, failure)
255 }
256}
257
258pub struct SendHandle {
263 invocation_id_future: BoxFuture<'static, Result<String, TerminalError>>,
264 ctx: ContextInternal,
265}
266
267impl SendHandle {
268 pub(crate) fn new(
269 ctx: ContextInternal,
270 invocation_id_future: BoxFuture<'static, Result<String, TerminalError>>,
271 ) -> Self {
272 Self {
273 invocation_id_future,
274 ctx,
275 }
276 }
277}
278
279impl IntoFuture for SendHandle {
280 type Output = Result<InvocationHandle, TerminalError>;
281 type IntoFuture = BoxFuture<'static, Result<InvocationHandle, TerminalError>>;
282
283 fn into_future(self) -> Self::IntoFuture {
284 let ctx = self.ctx;
285 async move {
286 let invocation_id = self.invocation_id_future.await?;
287 Ok(InvocationHandle::new(ctx, invocation_id))
288 }
289 .boxed()
290 }
291}
292
293pub trait CallFuture: DurableFuture<Output = Result<Self::Response, TerminalError>> {
294 type Response;
295
296 fn invocation_handle(
299 &self,
300 ) -> impl Future<Output = Result<InvocationHandle, TerminalError>> + Send;
301
302 #[deprecated(
304 since = "0.11.0",
305 note = "use `invocation_handle().await?.invocation_id()` instead"
306 )]
307 fn invocation_id(&self) -> impl Future<Output = Result<String, TerminalError>> + Send;
308}