use std::cell::RefCell;
use crate::AverStr;
use crate::aver_generated::domain::ast::FnDef;
use crate::aver_generated::domain::eval::callResolved;
use crate::aver_generated::domain::eval::store::{FnStore, fnsToStore, lookupFnOption};
use crate::aver_generated::domain::value::Val;
thread_local! {
static SELF_HOST_FN_STORE: RefCell<Option<FnStore>> = const { RefCell::new(None) };
}
pub fn with_fn_store<T, F>(fns: FnStore, run: F) -> T
where
F: FnOnce() -> T,
{
let previous = SELF_HOST_FN_STORE.with(|cell| cell.replace(Some(fns)));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(run));
SELF_HOST_FN_STORE.with(|cell| {
cell.replace(previous);
});
match result {
Ok(value) => value,
Err(payload) => std::panic::resume_unwind(payload),
}
}
pub fn with_program_fn_store<T, F>(
local_fns: crate::aver_rt::AverList<FnDef>,
module_fns: crate::aver_rt::AverList<FnDef>,
run: F,
) -> T
where
F: FnOnce() -> T,
{
let all_fns = crate::aver_rt::AverList::concat(&module_fns, &local_fns);
let fns = fnsToStore(&all_fns);
with_fn_store(fns, run)
}
fn current_fn_store() -> Option<FnStore> {
SELF_HOST_FN_STORE.with(|cell| cell.borrow().clone())
}
pub fn http_server_listen(port: i64, handler: Val) -> Result<(), AverStr> {
if crate::runtime_support::should_skip_http_server() {
return Ok(());
}
crate::aver_rt::http_server::listen(port, move |request| {
dispatch_http_handler(handler.clone(), None, request)
})
.map_err(AverStr::from)
}
pub fn http_server_listen_with(port: i64, context: Val, handler: Val) -> Result<(), AverStr> {
if crate::runtime_support::should_skip_http_server() {
return Ok(());
}
crate::aver_rt::http_server::listen_with(port, context, move |ctx, request| {
dispatch_http_handler(handler.clone(), Some(ctx), request)
})
.map_err(AverStr::from)
}
fn dispatch_http_handler(
handler: Val,
context: Option<Val>,
request: crate::aver_rt::HttpRequest,
) -> crate::aver_rt::HttpResponse {
let handler_name = match handler {
Val::ValFnRef(name) => name,
other => {
return http_error_response(format!(
"HttpServer handler must be a function reference, got {}",
crate::aver_rt::aver_display(&other)
));
}
};
let Some(fns) = current_fn_store() else {
return http_error_response(
"Self-host HttpServer callback store is not active".to_string(),
);
};
let Some(fd) = lookupFnOption(&fns, handler_name) else {
return http_error_response("Self-host HttpServer handler is not loaded".to_string());
};
let mut callback_args = Vec::new();
if let Some(ctx) = context {
callback_args.push(ctx);
}
callback_args.push(http_request_to_val(request));
let args_list = crate::aver_rt::AverList::from_vec(callback_args);
match callResolved(&fd, &args_list, &fns) {
Ok(value) => match val_to_http_response(value) {
Ok(resp) => resp,
Err(err) => http_error_response(err.to_string()),
},
Err(err) => http_error_response(err.to_string()),
}
}
fn http_request_to_val(request: crate::aver_rt::HttpRequest) -> Val {
Val::ValRecord(
AverStr::from("HttpRequest"),
crate::aver_rt::AverList::from_vec(vec![
(AverStr::from("method"), Val::ValStr(request.method)),
(AverStr::from("path"), Val::ValStr(request.path)),
(AverStr::from("body"), Val::ValStr(request.body)),
(AverStr::from("headers"), headers_to_val(request.headers)),
]),
)
}
fn headers_to_val(headers: crate::aver_rt::HttpHeaders) -> Val {
let mut out = crate::aver_rt::AverMap::default();
for (name, values) in headers.iter() {
let value_list =
crate::aver_rt::AverList::from_vec(values.iter().cloned().map(Val::ValStr).collect());
out = out.insert(name.clone(), Val::ValList(value_list));
}
Val::ValMap(out)
}
fn val_to_http_response(value: Val) -> Result<crate::aver_rt::HttpResponse, AverStr> {
let Val::ValRecord(type_name, fields) = value else {
return Err(AverStr::from(
"HttpServer handler must return HttpResponse record",
));
};
if &*type_name != "HttpResponse" {
return Err(AverStr::from(format!(
"HttpServer handler must return HttpResponse, got {}",
type_name
)));
}
let mut status = None;
let mut body = None;
let mut headers = crate::aver_rt::HttpHeaders::default();
for (field_name, field_value) in fields.to_vec() {
match &*field_name {
"status" => match field_value {
Val::ValInt(n) => status = Some(n),
_ => return Err(AverStr::from("HttpResponse.status must be Int")),
},
"body" => match field_value {
Val::ValStr(s) => body = Some(s),
_ => return Err(AverStr::from("HttpResponse.body must be String")),
},
"headers" => {
headers = val_to_headers(field_value)?;
}
_ => {}
}
}
Ok(crate::aver_rt::HttpResponse {
status: status.ok_or_else(|| AverStr::from("HttpResponse.status is required"))?,
body: body.ok_or_else(|| AverStr::from("HttpResponse.body is required"))?,
headers,
})
}
fn val_to_headers(value: Val) -> Result<crate::aver_rt::HttpHeaders, AverStr> {
let Val::ValMap(items) = value else {
return Err(AverStr::from(
"HttpResponse.headers must be Map<String, List<String>>",
));
};
let mut out = crate::aver_rt::HttpHeaders::default();
for (key, value) in items.iter() {
let Val::ValList(values) = value else {
return Err(AverStr::from(
"HttpResponse.headers values must be List<String>",
));
};
let mut value_strs = Vec::new();
for entry in values.to_vec() {
match entry {
Val::ValStr(s) => value_strs.push(s),
_ => {
return Err(AverStr::from(
"HttpResponse.headers value entries must be String",
));
}
}
}
out = out.insert(key.clone(), crate::aver_rt::AverList::from_vec(value_strs));
}
Ok(out)
}
fn http_error_response(message: String) -> crate::aver_rt::HttpResponse {
crate::aver_rt::HttpResponse {
status: 500,
body: AverStr::from(message),
headers: crate::aver_rt::HttpHeaders::default(),
}
}