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