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_typed_or_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_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
158 };
159 Rc::clone(&typed_endpoint.provider).describe(context, request)
160 }
161}
162
163#[derive(Debug)]
164pub struct EndpointHandle;
165impl RequestCapability for EndpointHandle {
166 type Request = HandleRequest;
167 type Response = HandleResponse;
168 type DomainError = HandleError;
169 const ID: &'static str = CAPABILITY_ID;
170 const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;
171
172 fn invoke_native(endpoint: &dyn NativeRequestEndpoint, operation: &str, request: Self::Request, context: InvocationContext) -> NativeRequestFuture<Self> {
173 if operation != HANDLE_OPERATION {
174 return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
175 }
176 let Some(typed_endpoint) = endpoint
177 .typed_endpoint()
178 .and_then(|endpoint| endpoint.downcast_ref::<EndpointRequestEndpoint>())
179 else {
180 return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
181 };
182 Rc::clone(&typed_endpoint.provider).handle(context, request)
183 }
184}
185
186impl serde::Serialize for DescribeError {
187 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
188 where
189 S: serde::Serializer,
190 {
191 use serde::ser::SerializeMap;
192 match self {
193 Self::InvalidConfiguration => serializer.serialize_str("invalid_configuration"),
194 Self::Unknown(value) => {
195 let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
196 map.serialize_entry("code", &value.code)?;
197 if let Some(payload) = &value.payload {
198 map.serialize_entry("payload", payload)?;
199 }
200 for (key, extra) in &value.extra {
201 map.serialize_entry(key, extra)?;
202 }
203 map.end()
204 },
205 }
206 }
207}
208
209impl<'de> serde::Deserialize<'de> for DescribeError {
210 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
211 where
212 D: serde::Deserializer<'de>,
213 {
214 let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
215 match value {
216 serde_json::Value::String(code) => match code.as_str() {
217 "invalid_configuration" => Ok(Self::InvalidConfiguration),
218 _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
219 },
220 serde_json::Value::Object(mut object) => {
221 let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
222 return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
223 };
224 let payload = object.remove("payload");
225 let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
226 Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
227 }
228 other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
229 }
230 }
231}
232
233impl serde::Serialize for HandleError {
234 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
235 where
236 S: serde::Serializer,
237 {
238 use serde::ser::SerializeMap;
239 match self {
240 Self::Rejected => serializer.serialize_str("rejected"),
241 Self::Unknown(value) => {
242 let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
243 map.serialize_entry("code", &value.code)?;
244 if let Some(payload) = &value.payload {
245 map.serialize_entry("payload", payload)?;
246 }
247 for (key, extra) in &value.extra {
248 map.serialize_entry(key, extra)?;
249 }
250 map.end()
251 },
252 }
253 }
254}
255
256impl<'de> serde::Deserialize<'de> for HandleError {
257 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
258 where
259 D: serde::Deserializer<'de>,
260 {
261 let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
262 match value {
263 serde_json::Value::String(code) => match code.as_str() {
264 "rejected" => Ok(Self::Rejected),
265 _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
266 },
267 serde_json::Value::Object(mut object) => {
268 let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
269 return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
270 };
271 let payload = object.remove("payload");
272 let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
273 Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
274 }
275 other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
276 }
277 }
278}
279
280pub fn encode_describe_request(value: &DescribeRequest) -> Result<String, serde_json::Error> { encode_portable_json(value) }
281pub fn decode_describe_request(wire: &str) -> Result<DescribeRequest, serde_json::Error> { decode_portable_json(wire) }
282pub fn encode_describe_response(value: &DescribeResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
283pub fn decode_describe_response(wire: &str) -> Result<DescribeResponse, serde_json::Error> { decode_portable_json(wire) }
284pub fn encode_describe_error(value: &DescribeError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
285pub fn decode_describe_error(wire: &str) -> Result<DescribeError, serde_json::Error> { decode_portable_json(wire) }
286
287pub fn encode_handle_request(value: &HandleRequest) -> Result<String, serde_json::Error> { encode_portable_json(value) }
288pub fn decode_handle_request(wire: &str) -> Result<HandleRequest, serde_json::Error> { decode_portable_json(wire) }
289pub fn encode_handle_response(value: &HandleResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
290pub fn decode_handle_response(wire: &str) -> Result<HandleResponse, serde_json::Error> { decode_portable_json(wire) }
291pub fn encode_handle_error(value: &HandleError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
292pub fn decode_handle_error(wire: &str) -> Result<HandleError, serde_json::Error> { decode_portable_json(wire) }
293
294pub trait EndpointProvider: fmt::Debug + 'static {
295 fn describe(&self, context: InvocationContext, request: DescribeRequest) -> NativeRequestFuture<EndpointDescribe>;
296 fn handle(&self, context: InvocationContext, request: HandleRequest) -> NativeRequestFuture<EndpointHandle>;
297}
298
299#[derive(Debug)]
300struct EndpointRequestEndpoint { provider: Rc<dyn EndpointProvider> }
301
302#[derive(Debug)]
303pub struct EndpointEndpoint<P: EndpointProvider> { provider: Rc<P>, request_endpoint: EndpointRequestEndpoint }
304impl<P: EndpointProvider> EndpointEndpoint<P> {
305 pub fn new(provider: P) -> Self {
306 let provider = Rc::new(provider);
307 let request_provider: Rc<dyn EndpointProvider> = provider.clone();
308 Self { provider, request_endpoint: EndpointRequestEndpoint { provider: request_provider } }
309 }
310}
311
312impl<P: EndpointProvider> NativeRequestEndpoint for EndpointEndpoint<P> {
313 fn capability_id(&self) -> &'static str { CAPABILITY_ID }
314 fn descriptor_version(&self) -> &'static str { DESCRIPTOR_VERSION }
315 fn operations(&self) -> &'static [&'static str] { &[
316 DESCRIBE_OPERATION,
317 HANDLE_OPERATION,
318 ] }
319 fn typed_endpoint(&self) -> Option<&dyn std::any::Any> { Some(&self.request_endpoint) }
320 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>> {
321 match operation {
322 DESCRIBE_OPERATION => {
323 let Ok(request) = request.downcast::<DescribeRequest>() else {
324 return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
325 };
326 let invocation = Rc::clone(&self.provider).describe(context, *request);
327 Box::pin(async move {
328 invocation.await.map(|result| {
329 result
330 .map(|value| Box::new(value) as Box<dyn std::any::Any>)
331 .map_err(|error| Box::new(error) as Box<dyn std::any::Any>)
332 })
333 })
334 },
335 HANDLE_OPERATION => {
336 let Ok(request) = request.downcast::<HandleRequest>() else {
337 return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
338 };
339 let invocation = Rc::clone(&self.provider).handle(context, *request);
340 Box::pin(async move {
341 invocation.await.map(|result| {
342 result
343 .map(|value| Box::new(value) as Box<dyn std::any::Any>)
344 .map_err(|error| Box::new(error) as Box<dyn std::any::Any>)
345 })
346 })
347 }
348 _ => Box::pin(futures::future::ready(Err(RuntimeFailure::UnknownOperation { capability: CAPABILITY_ID, operation: operation.to_owned() }))),
349 }
350 }
351}
352
353#[derive(Debug)]
354pub struct EndpointClient {
355 describe: NativeRequestHandle<EndpointDescribe>,
356 handle: NativeRequestHandle<EndpointHandle>,
357}
358impl EndpointClient {
359 pub fn from_dependencies(dependencies: &ModuleDependencies) -> Result<Self, RuntimeFailure> {
360 Ok(Self {
361 describe: dependencies.one::<EndpointDescribe>()?,
362 handle: dependencies.one::<EndpointHandle>()?,
363 })
364 }
365
366 pub async fn describe(&self, request: DescribeRequest) -> Result<DescribeResponse, EndpointDescribeInvocationError> {
367 self.describe.invoke(DESCRIBE_OPERATION, request).await
368 .map_err(EndpointDescribeInvocationError::Runtime)?
369 .map_err(EndpointDescribeInvocationError::Domain)
370 }
371
372 pub async fn describe_with_context(&self, context: InvocationContext, request: DescribeRequest) -> Result<DescribeResponse, EndpointDescribeInvocationError> {
373 self.describe.invoke_with_context(DESCRIBE_OPERATION, context, request).await
374 .map_err(EndpointDescribeInvocationError::Runtime)?
375 .map_err(EndpointDescribeInvocationError::Domain)
376 }
377
378 pub async fn handle(&self, request: HandleRequest) -> Result<HandleResponse, EndpointHandleInvocationError> {
379 self.handle.invoke(HANDLE_OPERATION, request).await
380 .map_err(EndpointHandleInvocationError::Runtime)?
381 .map_err(EndpointHandleInvocationError::Domain)
382 }
383
384 pub async fn handle_with_context(&self, context: InvocationContext, request: HandleRequest) -> Result<HandleResponse, EndpointHandleInvocationError> {
385 self.handle.invoke_with_context(HANDLE_OPERATION, context, request).await
386 .map_err(EndpointHandleInvocationError::Runtime)?
387 .map_err(EndpointHandleInvocationError::Domain)
388 }
389}
390
391#[derive(Clone, Debug, PartialEq)]
392pub enum EndpointDescribeInvocationError {
393 Domain(DescribeError),
394 Runtime(RuntimeFailure),
395}
396#[derive(Clone, Debug, PartialEq)]
397pub enum EndpointHandleInvocationError {
398 Domain(HandleError),
399 Runtime(RuntimeFailure),
400}