use alloc::vec::Vec;
mod error;
mod walk;
pub use error::DecisionBuildError;
mod levels;
use super::dom::vue::policy::{FileReads, NoReads};
use super::dom::{DomExpressionFacts, LiteralExpressions, build::DomBuilder};
use super::ssr::build::SsrBuilder;
use super::vapor::build::VaporBuilder;
use levels::Levels;
use super::{
ControlRegion, NativeAnalysis, NodeDecision,
policy::{BindingOwner, TargetPolicy},
};
use vize_l0::{id::NodeId, side_table::SideTable};
use vize_l2::{artifact::Artifact, file::FileArtifact, lang::js::NativeSelectedSetup};
pub fn build_decisions<'owner, 'arena>(
artifact: &'owner Artifact<'arena>,
policy: TargetPolicy,
) -> Result<NativeAnalysis<'owner, 'arena>, DecisionBuildError> {
build_with(artifact, policy, &LiteralExpressions, None, &NoReads)
}
pub fn build_dom_decisions<'owner, 'arena>(
artifact: &'owner Artifact<'arena>,
expressions: &impl DomExpressionFacts,
) -> Result<NativeAnalysis<'owner, 'arena>, DecisionBuildError> {
build_with(artifact, TargetPolicy::Dom, expressions, None, &NoReads)
}
pub(in crate::decision) fn build_with<'owner, 'arena>(
artifact: &'owner Artifact<'arena>,
policy: TargetPolicy,
expressions: &impl DomExpressionFacts,
file: Option<&'owner FileArtifact<'arena>>,
reads: &impl FileReads<'owner, 'arena>,
) -> Result<NativeAnalysis<'owner, 'arena>, DecisionBuildError> {
build_with_context(artifact, policy, expressions, file, reads, None, None)
}
pub(in crate::decision) fn build_with_ssr_setup<'owner, 'arena>(
setup: &NativeSelectedSetup<'owner, 'arena>,
) -> Result<NativeAnalysis<'owner, 'arena>, DecisionBuildError> {
let file = setup.file();
if !file.is_complete() {
return Err(DecisionBuildError::IncompleteFile);
}
build_with_context(
file.artifact(),
TargetPolicy::Ssr,
&LiteralExpressions,
Some(file),
&NoReads,
Some(setup),
None,
)
}
pub(in crate::decision) fn build_with_vapor_setup<'owner, 'arena>(
setup: &NativeSelectedSetup<'owner, 'arena>,
) -> Result<NativeAnalysis<'owner, 'arena>, DecisionBuildError> {
let file = setup.file();
build_with_context(
file.artifact(),
TargetPolicy::Vapor,
&LiteralExpressions,
Some(file),
&NoReads,
None,
Some(setup),
)
}
fn build_with_context<'owner, 'arena>(
artifact: &'owner Artifact<'arena>,
policy: TargetPolicy,
expressions: &impl DomExpressionFacts,
file: Option<&'owner FileArtifact<'arena>>,
reads: &impl FileReads<'owner, 'arena>,
ssr_setup: Option<&NativeSelectedSetup<'owner, 'arena>>,
vapor_setup: Option<&NativeSelectedSetup<'owner, 'arena>>,
) -> Result<NativeAnalysis<'owner, 'arena>, DecisionBuildError> {
let mut builder = Builder {
policy,
node_count: artifact.node_count(),
frames: Vec::new(),
nodes: SideTable::new(),
controls: SideTable::new(),
ssr: (policy == TargetPolicy::Ssr).then(|| match ssr_setup {
Some(setup) => SsrBuilder::new_setup(setup),
None => SsrBuilder::new(),
}),
vapor: (policy == TargetPolicy::Vapor).then(|| VaporBuilder::new(vapor_setup)),
dom: (policy == TargetPolicy::Dom)
.then(|| DomBuilder::new(expressions, artifact.root().ops.len(), file, reads)),
};
let mut failure = None;
artifact
.visit_events(&mut |event| {
if failure.is_none() {
failure = builder.visit(event).err();
}
})
.map_err(|_| DecisionBuildError::NodeLimit)?;
if let Some(error) = failure {
return Err(error);
}
let dom = builder.dom.take().map(DomBuilder::finish);
let ssr = builder.ssr.take().map(SsrBuilder::finish);
let vapor = builder.vapor.take().map(VaporBuilder::finish);
Ok(NativeAnalysis {
artifact,
tables: builder.finish()?,
dom,
ssr,
vapor,
})
}
struct Builder<'facts, 'owner, 'arena, F, R> {
policy: TargetPolicy,
node_count: u32,
frames: Vec<Frame>,
nodes: SideTable<NodeDecision>,
controls: SideTable<ControlRegion>,
dom: Option<DomBuilder<'facts, 'owner, 'arena, F, R>>,
ssr: Option<SsrBuilder<'facts, 'owner, 'arena>>,
vapor: Option<VaporBuilder<'facts, 'owner, 'arena>>,
}
struct Frame {
id: NodeId,
levels: Levels,
owner: Option<BindingOwner>,
control: Option<NodeId>,
child_control: Option<NodeId>,
dynamic_bindings: Vec<NodeId>,
}
#[cfg(test)]
mod tests;