1use std::{fmt, rc::Rc};
3use futures::future::LocalBoxFuture;
4use lenso_kernel::{InvocationContext, ModuleDependencies, NativeRequestEndpoint, NativeRequestFuture, NativeRequestHandle, RequestCapability, RuntimeFailure};
5
6pub const CAPABILITY_ID: &str = "lenso.http.endpoint@1";
7pub const DESCRIPTOR_VERSION: &str = "1.0.1";
8pub const PORTABLE: bool = true;
9pub const CROSS_LANE_TRANSFER: bool = true;
10pub const ENDPOINT_CAPABILITY_ID: &str = CAPABILITY_ID;
11pub const ENDPOINT_DESCRIPTOR_VERSION: &str = DESCRIPTOR_VERSION;
12
13pub const DESCRIBE_OPERATION: &str = "describe";
14pub const HANDLE_OPERATION: &str = "handle";
15
16pub use lenso_contract_runtime::{Bytes, UnknownDomainError};
17use lenso_contract_runtime::{decode_portable_json, encode_portable_json};
18
19#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
20pub struct DescribeRequest {
21
22}
23
24#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
25pub struct DescribeResponse {
26 #[serde(rename = "routes")]
27 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
28 pub routes: Vec<DescribeResponseRoutesItem>,
29}
30
31#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
32pub struct DescribeResponseRoutesItem {
33 #[serde(rename = "method")]
34 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
35 pub method: String,
36 #[serde(rename = "path")]
37 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
38 pub path: String,
39 #[serde(rename = "route_id")]
40 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
41 pub route_id: String,
42}
43
44#[derive(Clone, Debug, PartialEq)]
45pub enum DescribeError {
46 InvalidConfiguration,
47 Unknown(UnknownDomainError),
48}
49
50#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
51pub struct HandleRequest {
52 #[serde(rename = "body")]
53 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
54 pub body: Bytes,
55 #[serde(rename = "credential")]
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub credential: Option<HandleRequestCredential>,
58 #[serde(rename = "headers")]
59 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
60 pub headers: Vec<HandleRequestHeadersItem>,
61 #[serde(rename = "method")]
62 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
63 pub method: String,
64 #[serde(rename = "path")]
65 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
66 pub path: String,
67 #[serde(rename = "path_parameters")]
68 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
69 pub path_parameters: Vec<HandleRequestPathParametersItem>,
70 #[serde(rename = "query")]
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub query: Option<String>,
73 #[serde(rename = "request_id")]
74 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
75 pub request_id: String,
76 #[serde(rename = "route_id")]
77 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
78 pub route_id: String,
79}
80
81#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
82pub struct HandleRequestCredential {
83 #[serde(rename = "scheme")]
84 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
85 pub scheme: String,
86 #[serde(rename = "value")]
87 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
88 pub value: String,
89}
90
91#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
92pub struct HandleRequestHeadersItem {
93 #[serde(rename = "name")]
94 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
95 pub name: String,
96 #[serde(rename = "value")]
97 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
98 pub value: String,
99}
100
101#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
102pub struct HandleRequestPathParametersItem {
103 #[serde(rename = "name")]
104 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
105 pub name: String,
106 #[serde(rename = "value")]
107 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
108 pub value: String,
109}
110
111#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
112pub struct HandleResponse {
113 #[serde(rename = "body")]
114 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
115 pub body: Bytes,
116 #[serde(rename = "headers")]
117 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
118 pub headers: Vec<HandleResponseHeadersItem>,
119 #[serde(rename = "status")]
120 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
121 pub status: i64,
122}
123
124#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
125pub struct HandleResponseHeadersItem {
126 #[serde(rename = "name")]
127 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
128 pub name: String,
129 #[serde(rename = "value")]
130 #[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
131 pub value: String,
132}
133
134#[derive(Clone, Debug, PartialEq)]
135pub enum HandleError {
136 Rejected,
137 Unknown(UnknownDomainError),
138}
139
140#[derive(Debug)]
141pub struct EndpointDescribe;
142impl RequestCapability for EndpointDescribe {
143 type Request = DescribeRequest;
144 type Response = DescribeResponse;
145 type DomainError = DescribeError;
146 const ID: &'static str = CAPABILITY_ID;
147 const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;
148
149 fn invoke_native(endpoint: &dyn NativeRequestEndpoint, operation: &str, request: Self::Request, context: InvocationContext) -> NativeRequestFuture<Self> {
150 if operation != DESCRIBE_OPERATION {
151 return lenso_kernel::invoke_erased_native_request::<Self>(endpoint, operation, request, context);
152 }
153 let Some(typed_endpoint) = endpoint
154 .typed_endpoint()
155 .and_then(|endpoint| endpoint.downcast_ref::<EndpointRequestEndpoint>())
156 else {
157 return lenso_kernel::invoke_erased_native_request::<Self>(endpoint, operation, request, context);
158 };
159 let provider = Rc::clone(&typed_endpoint.provider);
160 Box::pin(async move {
161 match provider.describe(context, request).await {
162 Ok(value) => Ok(Ok(value)),
163 Err(EndpointDescribeInvocationError::Domain(error)) => Ok(Err(error)),
164 Err(EndpointDescribeInvocationError::Runtime(error)) => Err(error),
165 }
166 })
167 }
168}
169
170#[derive(Debug)]
171pub struct EndpointHandle;
172impl RequestCapability for EndpointHandle {
173 type Request = HandleRequest;
174 type Response = HandleResponse;
175 type DomainError = HandleError;
176 const ID: &'static str = CAPABILITY_ID;
177 const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;
178
179 fn invoke_native(endpoint: &dyn NativeRequestEndpoint, operation: &str, request: Self::Request, context: InvocationContext) -> NativeRequestFuture<Self> {
180 if operation != HANDLE_OPERATION {
181 return lenso_kernel::invoke_erased_native_request::<Self>(endpoint, operation, request, context);
182 }
183 let Some(typed_endpoint) = endpoint
184 .typed_endpoint()
185 .and_then(|endpoint| endpoint.downcast_ref::<EndpointRequestEndpoint>())
186 else {
187 return lenso_kernel::invoke_erased_native_request::<Self>(endpoint, operation, request, context);
188 };
189 let provider = Rc::clone(&typed_endpoint.provider);
190 Box::pin(async move {
191 match provider.handle(context, request).await {
192 Ok(value) => Ok(Ok(value)),
193 Err(EndpointHandleInvocationError::Domain(error)) => Ok(Err(error)),
194 Err(EndpointHandleInvocationError::Runtime(error)) => Err(error),
195 }
196 })
197 }
198}
199
200impl serde::Serialize for DescribeError {
201 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
202 where
203 S: serde::Serializer,
204 {
205 use serde::ser::SerializeMap;
206 match self {
207 Self::InvalidConfiguration => serializer.serialize_str("invalid_configuration"),
208 Self::Unknown(value) => {
209 let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
210 map.serialize_entry("code", &value.code)?;
211 if let Some(payload) = &value.payload {
212 map.serialize_entry("payload", payload)?;
213 }
214 for (key, extra) in &value.extra {
215 map.serialize_entry(key, extra)?;
216 }
217 map.end()
218 },
219 }
220 }
221}
222
223impl<'de> serde::Deserialize<'de> for DescribeError {
224 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
225 where
226 D: serde::Deserializer<'de>,
227 {
228 let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
229 match value {
230 serde_json::Value::String(code) => match code.as_str() {
231 "invalid_configuration" => Ok(Self::InvalidConfiguration),
232 _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
233 },
234 serde_json::Value::Object(mut object) => {
235 let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
236 return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
237 };
238 let payload = object.remove("payload");
239 let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
240 Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
241 }
242 other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
243 }
244 }
245}
246
247impl serde::Serialize for HandleError {
248 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249 where
250 S: serde::Serializer,
251 {
252 use serde::ser::SerializeMap;
253 match self {
254 Self::Rejected => serializer.serialize_str("rejected"),
255 Self::Unknown(value) => {
256 let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
257 map.serialize_entry("code", &value.code)?;
258 if let Some(payload) = &value.payload {
259 map.serialize_entry("payload", payload)?;
260 }
261 for (key, extra) in &value.extra {
262 map.serialize_entry(key, extra)?;
263 }
264 map.end()
265 },
266 }
267 }
268}
269
270impl<'de> serde::Deserialize<'de> for HandleError {
271 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
272 where
273 D: serde::Deserializer<'de>,
274 {
275 let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
276 match value {
277 serde_json::Value::String(code) => match code.as_str() {
278 "rejected" => Ok(Self::Rejected),
279 _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
280 },
281 serde_json::Value::Object(mut object) => {
282 let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
283 return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
284 };
285 let payload = object.remove("payload");
286 let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
287 Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
288 }
289 other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
290 }
291 }
292}
293
294pub fn encode_describe_request(value: &DescribeRequest) -> Result<String, serde_json::Error> { encode_portable_json(value) }
295pub fn decode_describe_request(wire: &str) -> Result<DescribeRequest, serde_json::Error> { decode_portable_json(wire) }
296pub fn encode_describe_response(value: &DescribeResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
297pub fn decode_describe_response(wire: &str) -> Result<DescribeResponse, serde_json::Error> { decode_portable_json(wire) }
298pub fn encode_describe_error(value: &DescribeError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
299pub fn decode_describe_error(wire: &str) -> Result<DescribeError, serde_json::Error> { decode_portable_json(wire) }
300
301pub fn encode_handle_request(value: &HandleRequest) -> Result<String, serde_json::Error> { encode_portable_json(value) }
302pub fn decode_handle_request(wire: &str) -> Result<HandleRequest, serde_json::Error> { decode_portable_json(wire) }
303pub fn encode_handle_response(value: &HandleResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
304pub fn decode_handle_response(wire: &str) -> Result<HandleResponse, serde_json::Error> { decode_portable_json(wire) }
305pub fn encode_handle_error(value: &HandleError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
306pub fn decode_handle_error(wire: &str) -> Result<HandleError, serde_json::Error> { decode_portable_json(wire) }
307
308pub trait EndpointProvider: fmt::Debug + 'static {
309 fn describe(&self, context: InvocationContext, request: DescribeRequest) -> LocalBoxFuture<'static, Result<DescribeResponse, EndpointDescribeInvocationError>>;
310 fn handle(&self, context: InvocationContext, request: HandleRequest) -> LocalBoxFuture<'static, Result<HandleResponse, EndpointHandleInvocationError>>;
311}
312
313#[derive(Debug)]
314struct EndpointRequestEndpoint { provider: Rc<dyn EndpointProvider> }
315
316#[derive(Debug)]
317pub struct EndpointEndpoint<P: EndpointProvider> { provider: Rc<P>, request_endpoint: EndpointRequestEndpoint }
318impl<P: EndpointProvider> EndpointEndpoint<P> {
319 pub fn new(provider: P) -> Self {
320 let provider = Rc::new(provider);
321 let request_provider: Rc<dyn EndpointProvider> = provider.clone();
322 Self { provider, request_endpoint: EndpointRequestEndpoint { provider: request_provider } }
323 }
324}
325
326impl<P: EndpointProvider> NativeRequestEndpoint for EndpointEndpoint<P> {
327 fn capability_id(&self) -> &'static str { CAPABILITY_ID }
328 fn descriptor_version(&self) -> &'static str { DESCRIPTOR_VERSION }
329 fn operations(&self) -> &'static [&'static str] { &[
330 DESCRIBE_OPERATION,
331 HANDLE_OPERATION,
332 ] }
333 fn typed_endpoint(&self) -> Option<&dyn std::any::Any> { Some(&self.request_endpoint) }
334 fn invoke(&self, operation: &str, request: Box<dyn std::any::Any>, context: InvocationContext) -> LocalBoxFuture<'static, Result<Result<Box<dyn std::any::Any>, Box<dyn std::any::Any>>, RuntimeFailure>> {
335 match operation {
336 DESCRIBE_OPERATION => {
337 let Ok(request) = request.downcast::<DescribeRequest>() else {
338 return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
339 };
340 let provider = Rc::clone(&self.provider);
341 Box::pin(async move {
342 match provider.describe(context, *request).await {
343 Ok(value) => Ok(Ok(Box::new(value) as Box<dyn std::any::Any>)),
344 Err(EndpointDescribeInvocationError::Domain(error)) => Ok(Err(Box::new(error) as Box<dyn std::any::Any>)),
345 Err(EndpointDescribeInvocationError::Runtime(error)) => Err(error),
346 }
347 })
348 },
349 HANDLE_OPERATION => {
350 let Ok(request) = request.downcast::<HandleRequest>() else {
351 return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
352 };
353 let provider = Rc::clone(&self.provider);
354 Box::pin(async move {
355 match provider.handle(context, *request).await {
356 Ok(value) => Ok(Ok(Box::new(value) as Box<dyn std::any::Any>)),
357 Err(EndpointHandleInvocationError::Domain(error)) => Ok(Err(Box::new(error) as Box<dyn std::any::Any>)),
358 Err(EndpointHandleInvocationError::Runtime(error)) => Err(error),
359 }
360 })
361 }
362 _ => Box::pin(futures::future::ready(Err(RuntimeFailure::UnknownOperation { capability: CAPABILITY_ID, operation: operation.to_owned() }))),
363 }
364 }
365}
366
367#[derive(Debug)]
368pub struct EndpointClient {
369 describe: NativeRequestHandle<EndpointDescribe>,
370 handle: NativeRequestHandle<EndpointHandle>,
371}
372impl EndpointClient {
373 pub fn from_dependencies(dependencies: &ModuleDependencies) -> Result<Self, RuntimeFailure> {
374 Ok(Self {
375 describe: dependencies.one::<EndpointDescribe>()?,
376 handle: dependencies.one::<EndpointHandle>()?,
377 })
378 }
379
380 pub async fn describe(&self, request: DescribeRequest) -> Result<DescribeResponse, EndpointDescribeInvocationError> {
381 self.describe.invoke(DESCRIBE_OPERATION, request).await
382 .map_err(EndpointDescribeInvocationError::Runtime)?
383 .map_err(EndpointDescribeInvocationError::Domain)
384 }
385
386 pub async fn describe_with_context(&self, context: InvocationContext, request: DescribeRequest) -> Result<DescribeResponse, EndpointDescribeInvocationError> {
387 self.describe.invoke_with_context(DESCRIBE_OPERATION, context, request).await
388 .map_err(EndpointDescribeInvocationError::Runtime)?
389 .map_err(EndpointDescribeInvocationError::Domain)
390 }
391
392 pub async fn handle(&self, request: HandleRequest) -> Result<HandleResponse, EndpointHandleInvocationError> {
393 self.handle.invoke(HANDLE_OPERATION, request).await
394 .map_err(EndpointHandleInvocationError::Runtime)?
395 .map_err(EndpointHandleInvocationError::Domain)
396 }
397
398 pub async fn handle_with_context(&self, context: InvocationContext, request: HandleRequest) -> Result<HandleResponse, EndpointHandleInvocationError> {
399 self.handle.invoke_with_context(HANDLE_OPERATION, context, request).await
400 .map_err(EndpointHandleInvocationError::Runtime)?
401 .map_err(EndpointHandleInvocationError::Domain)
402 }
403}
404
405#[derive(Clone, Debug, PartialEq)]
406pub enum EndpointDescribeInvocationError {
407 Domain(DescribeError),
408 Runtime(RuntimeFailure),
409}
410#[derive(Clone, Debug, PartialEq)]
411pub enum EndpointHandleInvocationError {
412 Domain(HandleError),
413 Runtime(RuntimeFailure),
414}