use rayon::prelude::*;
use std::borrow::Cow;
use webui_protocol::{CssStrategy, WebUIProtocol};
use super::graph::AssetGraphPlan;
use super::json::encode_json_string;
use crate::WebUIError;
pub(super) struct RenderedComponent<'a> {
pub resource: Option<RenderedStyleResource<'a>>,
pub template: Cow<'a, str>,
pub functions: Option<&'a str>,
}
#[derive(Clone, Copy)]
pub(super) enum RenderedStyleResource<'a> {
Link(&'a str),
Style(&'a str),
Module(&'a str),
}
pub(super) fn render_component_payloads<'a>(
protocol: &'a WebUIProtocol,
plan: &AssetGraphPlan,
) -> Result<Vec<Option<RenderedComponent<'a>>>, WebUIError> {
let mut payloads: Vec<Option<RenderedComponent<'a>>> = std::iter::repeat_with(|| None)
.take(plan.component_names.len())
.collect();
if plan.roots.len() == 1 {
for component in &plan.emitted_components {
payloads[*component] = Some(render_component(
protocol,
plan.component_names[*component],
)?);
}
return Ok(payloads);
}
let rendered: Vec<Result<(usize, RenderedComponent<'a>), WebUIError>> = plan
.emitted_components
.par_iter()
.map(|component| {
render_component(protocol, plan.component_names[*component])
.map(|payload| (*component, payload))
})
.collect();
for payload in rendered {
let (component, payload) = payload?;
payloads[component] = Some(payload);
}
Ok(payloads)
}
fn render_component<'a>(
protocol: &'a WebUIProtocol,
tag: &str,
) -> Result<RenderedComponent<'a>, WebUIError> {
let component = protocol.components.get(tag).ok_or_else(|| {
WebUIError::InvalidBuildOptions(format!(
"component asset payload for <{tag}> is missing protocol metadata"
))
})?;
let resource = render_style_resource(protocol, tag)?;
let template = if component.template_json.is_empty() {
Cow::Owned(encode_json_string(
&component.template,
"component template",
)?)
} else {
Cow::Borrowed(component.template_json.as_str())
};
let functions =
(!component.template_functions.is_empty()).then_some(component.template_functions.as_str());
Ok(RenderedComponent {
resource,
template,
functions,
})
}
pub(super) fn render_style_resource<'a>(
protocol: &'a WebUIProtocol,
tag: &str,
) -> Result<Option<RenderedStyleResource<'a>>, WebUIError> {
if !protocol.components.contains_key(tag) {
return Err(WebUIError::InvalidBuildOptions(format!(
"component asset style resource for <{tag}> is missing protocol metadata"
)));
}
let Some(resource) = protocol.component_style_resource(tag) else {
return Ok(None);
};
Ok(Some(match protocol.css_strategy() {
CssStrategy::Link => RenderedStyleResource::Link(resource),
CssStrategy::Style => RenderedStyleResource::Style(resource),
CssStrategy::Module => RenderedStyleResource::Module(resource),
}))
}