1use super::DurableFuture;
2
3use crate::endpoint::ContextInternal;
4use crate::errors::TerminalError;
5use crate::serde::{Deserialize, Serialize};
6use std::fmt;
7use std::future::Future;
8use std::marker::PhantomData;
9use std::time::Duration;
10
11#[derive(Debug, Clone)]
13pub enum RequestTarget {
14 Service {
15 name: String,
16 handler: String,
17 },
18 Object {
19 name: String,
20 key: String,
21 handler: String,
22 },
23 Workflow {
24 name: String,
25 key: String,
26 handler: String,
27 },
28}
29
30impl RequestTarget {
31 pub fn service(name: impl Into<String>, handler: impl Into<String>) -> Self {
32 Self::Service {
33 name: name.into(),
34 handler: handler.into(),
35 }
36 }
37
38 pub fn object(
39 name: impl Into<String>,
40 key: impl Into<String>,
41 handler: impl Into<String>,
42 ) -> Self {
43 Self::Object {
44 name: name.into(),
45 key: key.into(),
46 handler: handler.into(),
47 }
48 }
49
50 pub fn workflow(
51 name: impl Into<String>,
52 key: impl Into<String>,
53 handler: impl Into<String>,
54 ) -> Self {
55 Self::Workflow {
56 name: name.into(),
57 key: key.into(),
58 handler: handler.into(),
59 }
60 }
61}
62
63impl fmt::Display for RequestTarget {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 RequestTarget::Service { name, handler } => write!(f, "{name}/{handler}"),
67 RequestTarget::Object { name, key, handler } => write!(f, "{name}/{key}/{handler}"),
68 RequestTarget::Workflow { name, key, handler } => write!(f, "{name}/{key}/{handler}"),
69 }
70 }
71}
72
73pub struct Request<'a, Req, Res = ()> {
75 ctx: &'a ContextInternal,
76 request_target: RequestTarget,
77 idempotency_key: Option<String>,
78 headers: Vec<(String, String)>,
79 req: Req,
80 res: PhantomData<Res>,
81}
82
83impl<'a, Req, Res> Request<'a, Req, Res> {
84 pub(crate) fn new(ctx: &'a ContextInternal, request_target: RequestTarget, req: Req) -> Self {
85 Self {
86 ctx,
87 request_target,
88 idempotency_key: None,
89 headers: vec![],
90 req,
91 res: PhantomData,
92 }
93 }
94
95 pub fn header(mut self, key: String, value: String) -> Self {
96 self.headers.push((key, value));
97 self
98 }
99
100 pub fn idempotency_key(mut self, idempotency_key: impl Into<String>) -> Self {
102 self.idempotency_key = Some(idempotency_key.into());
103 self
104 }
105
106 pub fn call(self) -> impl CallFuture<Response = Res> + Send
108 where
109 Req: Serialize + 'static,
110 Res: Deserialize + 'static,
111 {
112 self.ctx.call(
113 self.request_target,
114 self.idempotency_key,
115 self.headers,
116 self.req,
117 )
118 }
119
120 pub fn send(self) -> impl InvocationHandle
122 where
123 Req: Serialize + 'static,
124 {
125 self.ctx.send(
126 self.request_target,
127 self.idempotency_key,
128 self.headers,
129 self.req,
130 None,
131 )
132 }
133
134 pub fn send_after(self, delay: Duration) -> impl InvocationHandle
136 where
137 Req: Serialize + 'static,
138 {
139 self.ctx.send(
140 self.request_target,
141 self.idempotency_key,
142 self.headers,
143 self.req,
144 Some(delay),
145 )
146 }
147}
148
149pub trait InvocationHandle {
150 fn invocation_id(&self) -> impl Future<Output = Result<String, TerminalError>> + Send;
151 fn cancel(&self) -> impl Future<Output = Result<(), TerminalError>> + Send;
152}
153
154pub trait CallFuture:
155 DurableFuture<Output = Result<Self::Response, TerminalError>> + InvocationHandle
156{
157 type Response;
158}