use crate::error::{ErrorData, Result};
use crate::traits::{Binding, Worker, WorkerInvokeRequest, WorkerInvokeResponse};
use alien_core::bindings::LocalWorkerBinding;
use alien_error::{Context, IntoAlienError};
use async_trait::async_trait;
use std::collections::BTreeMap;
#[derive(Debug)]
pub struct LocalWorker {
binding: LocalWorkerBinding,
}
impl LocalWorker {
pub fn new(binding: LocalWorkerBinding) -> Self {
Self { binding }
}
fn get_worker_url(&self) -> Result<String> {
self.binding
.worker_url
.clone()
.into_value("worker", "worker_url")
.context(ErrorData::BindingConfigInvalid {
binding_name: "worker".to_string(),
reason: "Failed to resolve worker_url from binding".to_string(),
})
}
}
impl Binding for LocalWorker {}
#[async_trait]
impl Worker for LocalWorker {
async fn invoke(&self, request: WorkerInvokeRequest) -> Result<WorkerInvokeResponse> {
let worker_url = self.get_worker_url()?;
let target_url = if !request.target_worker.is_empty() {
format!(
"{}/{}",
worker_url.trim_end_matches('/'),
request.target_worker
)
} else {
worker_url
};
let full_url = if !request.path.is_empty() {
format!(
"{}/{}",
target_url.trim_end_matches('/'),
request.path.trim_start_matches('/')
)
} else {
target_url
};
let client = reqwest::Client::new();
let mut http_request = client.request(
reqwest::Method::from_bytes(request.method.as_bytes())
.into_alien_error()
.context(ErrorData::BindingConfigInvalid {
binding_name: "worker".to_string(),
reason: format!("Invalid HTTP method: {}", request.method),
})?,
&full_url,
);
for (key, value) in request.headers {
http_request = http_request.header(key, value);
}
if !request.body.is_empty() {
http_request = http_request.body(request.body);
}
if let Some(timeout) = request.timeout {
http_request = http_request.timeout(timeout);
}
let response = http_request.send().await.into_alien_error().context(
ErrorData::CloudPlatformError {
message: format!("Failed to invoke local worker at: {}", full_url),
resource_id: None,
},
)?;
let status = response.status().as_u16();
let mut headers = BTreeMap::new();
for (key, value) in response.headers() {
if let Ok(value_str) = value.to_str() {
headers.insert(key.to_string(), value_str.to_string());
}
}
let body = response
.bytes()
.await
.into_alien_error()
.context(ErrorData::CloudPlatformError {
message: "Failed to read response body from local worker".to_string(),
resource_id: None,
})?
.to_vec();
Ok(WorkerInvokeResponse {
status,
headers,
body,
})
}
async fn get_worker_url(&self) -> Result<Option<String>> {
Ok(Some(self.get_worker_url()?))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}