alien_bindings/providers/function/
local.rs1use crate::error::{ErrorData, Result};
2use crate::traits::{Binding, Function, FunctionInvokeRequest, FunctionInvokeResponse};
3use alien_core::bindings::LocalFunctionBinding;
4use alien_error::{Context, IntoAlienError};
5use async_trait::async_trait;
6use std::collections::BTreeMap;
7
8#[derive(Debug)]
13pub struct LocalFunction {
14 binding: LocalFunctionBinding,
15}
16
17impl LocalFunction {
18 pub fn new(binding: LocalFunctionBinding) -> Self {
20 Self { binding }
21 }
22
23 fn get_function_url(&self) -> Result<String> {
25 self.binding
26 .function_url
27 .clone()
28 .into_value("function", "function_url")
29 .context(ErrorData::BindingConfigInvalid {
30 binding_name: "function".to_string(),
31 reason: "Failed to resolve function_url from binding".to_string(),
32 })
33 }
34}
35
36impl Binding for LocalFunction {}
37
38#[async_trait]
39impl Function for LocalFunction {
40 async fn invoke(&self, request: FunctionInvokeRequest) -> Result<FunctionInvokeResponse> {
41 let function_url = self.get_function_url()?;
42
43 let target_url = if !request.target_function.is_empty() {
45 format!(
46 "{}/{}",
47 function_url.trim_end_matches('/'),
48 request.target_function
49 )
50 } else {
51 function_url
52 };
53
54 let full_url = if !request.path.is_empty() {
56 format!(
57 "{}/{}",
58 target_url.trim_end_matches('/'),
59 request.path.trim_start_matches('/')
60 )
61 } else {
62 target_url
63 };
64
65 let client = reqwest::Client::new();
67
68 let mut http_request = client.request(
70 reqwest::Method::from_bytes(request.method.as_bytes())
71 .into_alien_error()
72 .context(ErrorData::BindingConfigInvalid {
73 binding_name: "function".to_string(),
74 reason: format!("Invalid HTTP method: {}", request.method),
75 })?,
76 &full_url,
77 );
78
79 for (key, value) in request.headers {
81 http_request = http_request.header(key, value);
82 }
83
84 if !request.body.is_empty() {
86 http_request = http_request.body(request.body);
87 }
88
89 if let Some(timeout) = request.timeout {
91 http_request = http_request.timeout(timeout);
92 }
93
94 let response = http_request.send().await.into_alien_error().context(
96 ErrorData::CloudPlatformError {
97 message: format!("Failed to invoke local function at: {}", full_url),
98 resource_id: None,
99 },
100 )?;
101
102 let status = response.status().as_u16();
104 let mut headers = BTreeMap::new();
105
106 for (key, value) in response.headers() {
107 if let Ok(value_str) = value.to_str() {
108 headers.insert(key.to_string(), value_str.to_string());
109 }
110 }
111
112 let body = response
113 .bytes()
114 .await
115 .into_alien_error()
116 .context(ErrorData::CloudPlatformError {
117 message: "Failed to read response body from local function".to_string(),
118 resource_id: None,
119 })?
120 .to_vec();
121
122 Ok(FunctionInvokeResponse {
123 status,
124 headers,
125 body,
126 })
127 }
128
129 async fn get_function_url(&self) -> Result<Option<String>> {
130 Ok(Some(self.get_function_url()?))
131 }
132
133 fn as_any(&self) -> &dyn std::any::Any {
134 self
135 }
136}