use std::future::Future;
use fraiseql_error::Result;
use crate::types::{EventPayload, LogEntry, LogLevel};
#[cfg(feature = "host-live")]
pub mod live;
#[cfg(feature = "host-live")]
pub mod factory;
#[derive(Debug, Clone)]
pub struct HttpResponse {
pub status: u16,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
}
#[allow(clippy::trait_duplication_in_bounds)] #[trait_variant::make(SendHostContext: Send)]
pub trait HostContext: Send + Sync {
fn query(
&self,
graphql: &str,
variables: serde_json::Value,
) -> impl Future<Output = Result<serde_json::Value>> + Send;
fn sql_query(
&self,
sql: &str,
params: &[serde_json::Value],
) -> impl Future<Output = Result<Vec<serde_json::Value>>> + Send;
fn http_request(
&self,
method: &str,
url: &str,
headers: &[(String, String)],
body: Option<&[u8]>,
) -> impl Future<Output = Result<HttpResponse>> + Send;
fn storage_get(&self, bucket: &str, key: &str) -> impl Future<Output = Result<Vec<u8>>> + Send;
fn storage_put(
&self,
bucket: &str,
key: &str,
body: &[u8],
content_type: &str,
) -> impl Future<Output = Result<()>> + Send;
fn auth_context(&self) -> Result<serde_json::Value>;
fn env_var(&self, name: &str) -> Result<Option<String>>;
fn event_payload(&self) -> &EventPayload;
fn log(&self, level: LogLevel, message: &str);
}
pub struct NoopHostContext {
event_payload: EventPayload,
logs: std::sync::Arc<std::sync::Mutex<Vec<LogEntry>>>,
}
impl NoopHostContext {
#[must_use]
pub fn new(event_payload: EventPayload) -> Self {
Self {
event_payload,
logs: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
}
}
#[must_use]
pub fn captured_logs(&self) -> Vec<LogEntry> {
self.logs.lock().expect("log mutex poisoned").clone()
}
}
impl HostContext for NoopHostContext {
async fn query(
&self,
_graphql: &str,
_variables: serde_json::Value,
) -> Result<serde_json::Value> {
Err(fraiseql_error::FraiseQLError::Unsupported {
message: "HostContext::query not implemented".to_string(),
})
}
async fn sql_query(
&self,
_sql: &str,
_params: &[serde_json::Value],
) -> Result<Vec<serde_json::Value>> {
Err(fraiseql_error::FraiseQLError::Unsupported {
message: "HostContext::sql_query not implemented".to_string(),
})
}
async fn http_request(
&self,
_method: &str,
_url: &str,
_headers: &[(String, String)],
_body: Option<&[u8]>,
) -> Result<HttpResponse> {
Err(fraiseql_error::FraiseQLError::Unsupported {
message: "HostContext::http_request not implemented".to_string(),
})
}
async fn storage_get(&self, _bucket: &str, _key: &str) -> Result<Vec<u8>> {
Err(fraiseql_error::FraiseQLError::Unsupported {
message: "HostContext::storage_get not implemented".to_string(),
})
}
async fn storage_put(
&self,
_bucket: &str,
_key: &str,
_body: &[u8],
_content_type: &str,
) -> Result<()> {
Err(fraiseql_error::FraiseQLError::Unsupported {
message: "HostContext::storage_put not implemented".to_string(),
})
}
fn auth_context(&self) -> Result<serde_json::Value> {
Err(fraiseql_error::FraiseQLError::Unsupported {
message: "HostContext::auth_context not implemented".to_string(),
})
}
fn env_var(&self, _name: &str) -> Result<Option<String>> {
Ok(None)
}
fn event_payload(&self) -> &EventPayload {
&self.event_payload
}
fn log(&self, level: LogLevel, message: &str) {
let entry = LogEntry {
level,
message: message.to_string(),
timestamp: chrono::Utc::now(),
};
self.logs.lock().expect("log mutex poisoned").push(entry);
}
}
#[cfg(test)]
mod tests;