use alloc::vec::Vec;
use vize_l0::{Allocator, Box, Span, id::NodeId, side_table::SideTable};
use super::{Artifact, ArtifactError, ArtifactParts, RejectedArtifact, check};
use crate::expr::ExprRef;
use crate::op::{Attribute, BindingOp, CommentOp, InterpolationOp, Namespace, Op, Region};
use crate::provenance::ProvenanceRecord;
use crate::walk::PageWalk;
struct Frame<'a> {
id: NodeId,
owner: Owner<'a>,
span: Span,
bindings: vize_l0::Vec<'a, BindingOp<'a>>,
children_started: bool,
ops: vize_l0::Vec<'a, Op<'a>>,
}
enum Owner<'a> {
OriginalFor(crate::op::OriginalForId),
Element {
tag: &'a str,
namespace: Namespace,
attributes: vize_l0::Vec<'a, Attribute<'a>>,
},
Component {
name: &'a str,
attributes: vize_l0::Vec<'a, Attribute<'a>>,
},
}
mod binding;
mod close;
mod element;
pub(crate) use element::ElementAllocation;
mod factory;
mod original_for;
mod region;
mod text;
pub use factory::{ComponentBody, ComponentFactory};
pub use region::RegionBuilder;
pub(crate) use text::TextAllocation;
pub struct Builder<'a> {
allocator: &'a Allocator,
parts: ArtifactParts<'a>,
frames: Vec<Frame<'a>>,
walk: PageWalk,
}
impl<'a> Builder<'a> {
pub fn region(&mut self) -> RegionBuilder<'_, 'a> {
RegionBuilder { builder: self }
}
pub fn new(allocator: &'a Allocator, source: &'a str) -> Result<Self, ArtifactError> {
u32::try_from(source.len()).map_err(|_| ArtifactError::SourceLimit)?;
Ok(Self {
allocator,
parts: ArtifactParts {
source,
root: Region {
ops: vize_l0::Vec::new_in(&allocator),
},
provenance: Vec::new(),
scopes: SideTable::new(),
},
frames: Vec::new(),
walk: PageWalk::new(),
})
}
pub fn comment(&mut self, content: &'a str, span: Span) -> Result<NodeId, ArtifactError> {
let id = self.prepare(span)?;
self.mint();
self.push(Op::Comment(Box::new_in(
CommentOp { content, span },
&self.allocator,
)));
Ok(id)
}
pub fn interpolation(
&mut self,
expression: ExprRef<'a>,
span: Span,
) -> Result<NodeId, ArtifactError> {
let id = self.prepare(span)?;
check::expression(self.parts.source, id, expression, span)?;
self.mint();
self.push(Op::Interpolation(Box::new_in(
InterpolationOp { expression, span },
&self.allocator,
)));
Ok(id)
}
pub fn element(
&mut self,
tag: &'a str,
namespace: Namespace,
attributes: vize_l0::Vec<'a, Attribute<'a>>,
span: Span,
children: impl FnOnce(&mut RegionBuilder<'_, 'a>, NodeId),
) -> Result<NodeId, ArtifactError> {
let id = self.prepare_owner(&attributes, span)?;
self.children(
Owner::Element {
tag,
namespace,
attributes,
},
span,
id,
children,
)?;
Ok(id)
}
pub fn component(
&mut self,
name: &'a str,
attributes: vize_l0::Vec<'a, Attribute<'a>>,
span: Span,
children: impl FnOnce(&mut RegionBuilder<'_, 'a>, NodeId),
) -> Result<NodeId, ArtifactError> {
let id = self.prepare_owner(&attributes, span)?;
self.children(Owner::Component { name, attributes }, span, id, children)?;
Ok(id)
}
pub fn record(&mut self, record: ProvenanceRecord) -> Result<(), ArtifactError> {
check::span(self.parts.source, record.node, record.span)?;
if let Some(node) = record.node
&& node.index() >= self.walk.minted()
{
return Err(ArtifactError::DanglingProvenance {
record: self.parts.provenance.len(),
node,
});
}
self.parts.provenance.push(record);
Ok(())
}
pub fn finish(mut self) -> Result<Artifact<'a>, RejectedArtifact<'a>> {
if let Some(node) = self.frames.last().map(|frame| frame.id) {
while let Some(frame) = self.frames.pop() {
let _ = self.close(frame);
}
return Err(RejectedArtifact {
error: ArtifactError::UnfinishedOwner { node },
parts: alloc::boxed::Box::new(self.parts),
});
}
Ok(Artifact {
parts: self.parts,
node_count: self.walk.minted(),
})
}
fn prepare(&self, span: Span) -> Result<NodeId, ArtifactError> {
let id = NodeId::from_index(self.walk.minted()).ok_or(ArtifactError::NodeLimit)?;
check::owned_span(
self.parts.source,
id,
span,
self.frames.last().map(|frame| frame.span),
)?;
Ok(id)
}
fn prepare_owner(
&self,
attributes: &[Attribute<'a>],
span: Span,
) -> Result<NodeId, ArtifactError> {
let id = self.prepare(span)?;
for attribute in attributes {
check::owned_span(self.parts.source, id, attribute.span, Some(span))?;
}
Ok(id)
}
fn mint(&mut self) {
if let Some(frame) = self.frames.last_mut() {
frame.children_started = true;
}
let _ = self.walk.mint();
}
fn children(
&mut self,
owner: Owner<'a>,
span: Span,
id: NodeId,
children: impl FnOnce(&mut RegionBuilder<'_, 'a>, NodeId),
) -> Result<close::ClosedFrame<'a>, ArtifactError> {
self.mint();
self.frames.push(Frame {
id,
owner,
span,
bindings: vize_l0::Vec::new_in(&self.allocator),
children_started: false,
ops: vize_l0::Vec::new_in(&self.allocator),
});
children(&mut RegionBuilder { builder: self }, id);
let pending = self.frames.last().map(|frame| frame.id);
if pending != Some(id) {
return Err(ArtifactError::UnfinishedOwner {
node: pending.unwrap_or(id),
});
}
let frame = self
.frames
.pop()
.ok_or(ArtifactError::UnfinishedOwner { node: id })?;
Ok(self.close(frame))
}
fn push(&mut self, op: Op<'a>) {
match self.frames.last_mut() {
Some(frame) => frame.ops.push(op),
None => self.parts.root.ops.push(op),
}
}
}
#[cfg(test)]
mod tests;