use crate::{ResponseWriter, WebUIHandler};
use webui_handler::route_handler;
use webui_handler::RenderOptions;
use webui_protocol::WebUIProtocol;
pub struct ServeRequest<'a> {
pub path: &'a str,
pub accept_json: bool,
pub inventory_hex: &'a str,
}
pub enum ServeResponse {
Html(String),
Json(serde_json::Value),
}
pub fn serve_request(
protocol: &WebUIProtocol,
handler: &WebUIHandler,
state: serde_json::Value,
entry: &str,
request: &ServeRequest<'_>,
) -> Result<ServeResponse, String> {
let params = route_handler::collect_nested_route_params(protocol, entry, request.path);
let mut data = state;
if let Some(map) = data.as_object_mut() {
for (k, v) in ¶ms {
map.insert(k.clone(), serde_json::Value::String(v.clone()));
}
}
if request.accept_json {
let partial = route_handler::render_partial(
protocol,
data,
entry,
request.path,
request.inventory_hex,
);
let (needed, new_inv) = route_handler::get_needed_components_for_request(
protocol,
entry,
request.path,
request.inventory_hex,
);
let templates: Vec<serde_json::Value> = needed
.iter()
.filter_map(|name| {
protocol
.components
.get(name)
.map(|c| c.template.as_str())
.filter(|s| !s.is_empty())
})
.map(|t| serde_json::Value::String(t.to_string()))
.collect();
let mut resp = match partial {
serde_json::Value::Object(m) => m,
_ => serde_json::Map::new(),
};
resp.insert("templates".into(), serde_json::Value::Array(templates));
resp.insert("inventory".into(), serde_json::Value::String(new_inv));
Ok(ServeResponse::Json(serde_json::Value::Object(resp)))
} else {
let mut writer = MemWriter::with_capacity(131_072);
let opts = RenderOptions::new(entry, request.path);
handler
.handle(protocol, &data, &opts, &mut writer)
.map_err(|e| format!("render failed: {e}"))?;
let state_json =
serde_json::to_string(&data).map_err(|e| format!("serialize failed: {e}"))?;
let safe_json = state_json.replace("</", "<\\/");
let script = format!("<script>window.__webui_state={safe_json}</script>");
let html = writer.buf.replace("</body>", &format!("{script}</body>"));
Ok(ServeResponse::Html(html))
}
}
struct MemWriter {
buf: String,
}
impl MemWriter {
fn with_capacity(cap: usize) -> Self {
Self {
buf: String::with_capacity(cap),
}
}
}
impl ResponseWriter for MemWriter {
fn write(&mut self, content: &str) -> webui_handler::Result<()> {
self.buf.push_str(content);
Ok(())
}
fn end(&mut self) -> webui_handler::Result<()> {
Ok(())
}
}