1use std::{future::Future, pin::Pin};
14
15use fraiseql_error::Result;
16
17use crate::host::HttpResponse;
18
19pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
21
22pub trait DynHostContext: Send + Sync {
27 fn query(
29 &self,
30 graphql: &str,
31 variables: serde_json::Value,
32 ) -> BoxFuture<'_, Result<serde_json::Value>>;
33
34 fn sql_query(
36 &self,
37 sql: &str,
38 params: &[serde_json::Value],
39 ) -> BoxFuture<'_, Result<Vec<serde_json::Value>>>;
40
41 fn http_request(
43 &self,
44 method: &str,
45 url: &str,
46 headers: &[(String, String)],
47 body: Option<&[u8]>,
48 ) -> BoxFuture<'_, Result<HttpResponse>>;
49
50 fn storage_get(&self, bucket: &str, key: &str) -> BoxFuture<'_, Result<Vec<u8>>>;
52
53 fn storage_put(
55 &self,
56 bucket: &str,
57 key: &str,
58 body: &[u8],
59 content_type: &str,
60 ) -> BoxFuture<'_, Result<()>>;
61
62 fn send_email<'a>(
64 &'a self,
65 request: &'a crate::outbound::SendEmailRequest,
66 ) -> BoxFuture<'a, Result<crate::outbound::SendEmailResponse>>;
67
68 fn auth_context(&self) -> Result<serde_json::Value>;
74
75 fn env_var(&self, name: &str) -> Result<Option<String>>;
81
82 fn event_payload(&self) -> &crate::types::EventPayload;
84
85 fn log(&self, level: crate::types::LogLevel, message: &str);
87
88 fn idempotency_token(&self) -> Option<String> {
94 None
95 }
96
97 fn cursor(&self) -> BoxFuture<'_, Result<Option<serde_json::Value>>> {
100 Box::pin(async { Ok(None) })
101 }
102
103 fn advance_cursor(&self, value: serde_json::Value) -> BoxFuture<'_, Result<()>> {
107 Box::pin(async move {
108 let _ = value;
109 Err(fraiseql_error::FraiseQLError::validation(
110 "advance_cursor: this function is not a scheduled source (no cursor binding)",
111 ))
112 })
113 }
114}
115
116impl<T: crate::HostContext + Send + Sync> DynHostContext for T {
118 fn query(
119 &self,
120 graphql: &str,
121 variables: serde_json::Value,
122 ) -> BoxFuture<'_, Result<serde_json::Value>> {
123 let graphql = graphql.to_string();
125 Box::pin(async move { crate::HostContext::query(self, &graphql, variables).await })
126 }
127
128 fn sql_query(
129 &self,
130 sql: &str,
131 params: &[serde_json::Value],
132 ) -> BoxFuture<'_, Result<Vec<serde_json::Value>>> {
133 let sql = sql.to_string();
134 let params = params.to_vec();
135 Box::pin(async move { crate::HostContext::sql_query(self, &sql, ¶ms).await })
136 }
137
138 fn http_request(
139 &self,
140 method: &str,
141 url: &str,
142 headers: &[(String, String)],
143 body: Option<&[u8]>,
144 ) -> BoxFuture<'_, Result<HttpResponse>> {
145 let method = method.to_string();
146 let url = url.to_string();
147 let headers = headers.to_vec();
148 let body = body.map(<[u8]>::to_vec);
149 Box::pin(async move {
150 crate::HostContext::http_request(self, &method, &url, &headers, body.as_deref()).await
151 })
152 }
153
154 fn storage_get(&self, bucket: &str, key: &str) -> BoxFuture<'_, Result<Vec<u8>>> {
155 let bucket = bucket.to_string();
156 let key = key.to_string();
157 Box::pin(async move { crate::HostContext::storage_get(self, &bucket, &key).await })
158 }
159
160 fn storage_put(
161 &self,
162 bucket: &str,
163 key: &str,
164 body: &[u8],
165 content_type: &str,
166 ) -> BoxFuture<'_, Result<()>> {
167 let bucket = bucket.to_string();
168 let key = key.to_string();
169 let body = body.to_vec();
170 let content_type = content_type.to_string();
171 Box::pin(async move {
172 crate::HostContext::storage_put(self, &bucket, &key, &body, &content_type).await
173 })
174 }
175
176 fn send_email<'a>(
177 &'a self,
178 request: &'a crate::outbound::SendEmailRequest,
179 ) -> BoxFuture<'a, Result<crate::outbound::SendEmailResponse>> {
180 let request = request.clone();
181 Box::pin(async move { crate::HostContext::send_email(self, &request).await })
182 }
183
184 fn auth_context(&self) -> Result<serde_json::Value> {
185 crate::HostContext::auth_context(self)
186 }
187
188 fn env_var(&self, name: &str) -> Result<Option<String>> {
189 crate::HostContext::env_var(self, name)
190 }
191
192 fn event_payload(&self) -> &crate::types::EventPayload {
193 crate::HostContext::event_payload(self)
194 }
195
196 fn log(&self, level: crate::types::LogLevel, message: &str) {
197 crate::HostContext::log(self, level, message);
198 }
199
200 fn idempotency_token(&self) -> Option<String> {
201 crate::HostContext::idempotency_token(self)
202 }
203
204 fn cursor(&self) -> BoxFuture<'_, Result<Option<serde_json::Value>>> {
205 Box::pin(async move { crate::HostContext::cursor(self).await })
206 }
207
208 fn advance_cursor(&self, value: serde_json::Value) -> BoxFuture<'_, Result<()>> {
209 Box::pin(async move { crate::HostContext::advance_cursor(self, value).await })
210 }
211}