use alloc::vec::Vec;
use vize_l0::{Span, String, ensure_sufficient_stack};
use crate::dump::Page;
use crate::op::{Attribute as IrAttribute, DynamicName, Namespace, Op as IrOp, Region};
mod binding;
mod expr;
pub use crate::dump::owned::binding::{
Bind, Binding, Model, On, SlotContent, VueCloak, VueCssBind, VueDirective, VueHtml, VueMemo,
VueOnce, VueShow, VueSlotScope, VueSync, VueText,
};
pub use crate::dump::owned::expr::{Contract, Expr, ForBinding};
use binding::own_binding;
use expr::own_expr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Op {
Element(Element),
Component(Component),
Text(Text),
Interpolation(Interpolation),
Comment(Comment),
If(If),
For(For),
OriginalFor(OriginalFor),
Slot(Slot),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Name {
Static(String),
Dynamic(Expr),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attribute {
pub name: String,
pub value: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Element {
pub tag: String,
pub namespace: Namespace,
pub attributes: Vec<Attribute>,
pub bindings: Vec<Binding>,
pub children: Vec<Op>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Component {
pub name: String,
pub attributes: Vec<Attribute>,
pub bindings: Vec<Binding>,
pub children: Vec<Op>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Text {
pub content: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Interpolation {
pub expression: Expr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Comment {
pub content: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct If {
pub branches: Vec<Branch>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Branch {
pub condition: Option<Expr>,
pub ops: Vec<Op>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct For {
pub binding: ForBinding,
pub ops: Vec<Op>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OriginalFor {
pub node: u32,
pub ops: Vec<Op>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Slot {
pub name: Name,
pub attributes: Vec<Attribute>,
pub bindings: Vec<Binding>,
pub fallback: Vec<Op>,
pub span: Span,
}
impl Page {
#[must_use]
pub fn of(ops: &[IrOp<'_>]) -> Self {
ensure_sufficient_stack(|| Self { ops: own_ops(ops) })
}
#[must_use]
pub fn op_count(&self) -> u64 {
ensure_sufficient_stack(|| self.ops.iter().map(count_op).sum())
}
}
fn own_ops(ops: &[IrOp<'_>]) -> Vec<Op> {
ops.iter()
.map(|op| ensure_sufficient_stack(|| own_op(op)))
.collect()
}
fn own_region(region: &Region<'_>) -> Vec<Op> {
ensure_sufficient_stack(|| own_ops(®ion.ops))
}
fn own_op(op: &IrOp<'_>) -> Op {
ensure_sufficient_stack(|| own_op_guarded(op))
}
fn own_op_guarded(op: &IrOp<'_>) -> Op {
match op {
IrOp::Element(element) => Op::Element(Element {
tag: String::from(element.tag),
namespace: element.namespace,
attributes: element.attributes.iter().map(own_attribute).collect(),
bindings: element.bindings.iter().map(own_binding).collect(),
children: own_region(&element.children),
span: element.span,
}),
IrOp::Component(component) => Op::Component(Component {
name: String::from(component.name),
attributes: component.attributes.iter().map(own_attribute).collect(),
bindings: component.bindings.iter().map(own_binding).collect(),
children: own_region(&component.children),
span: component.span,
}),
IrOp::Text(text) => Op::Text(Text {
content: String::from(text.content),
span: text.span,
}),
IrOp::Interpolation(interpolation) => Op::Interpolation(Interpolation {
expression: own_expr(&interpolation.expression),
span: interpolation.span,
}),
IrOp::Comment(comment) => Op::Comment(Comment {
content: String::from(comment.content),
span: comment.span,
}),
IrOp::If(if_op) => Op::If(If {
branches: if_op
.branches
.iter()
.map(|branch| Branch {
condition: branch.condition.as_ref().map(own_expr),
ops: own_region(&branch.region),
span: branch.span,
})
.collect(),
span: if_op.span,
}),
IrOp::For(for_op) => Op::For(For {
binding: ForBinding {
source: own_expr(&for_op.binding.source),
value: own_expr(&for_op.binding.value),
key: for_op.binding.key.as_ref().map(own_expr),
index: for_op.binding.index.as_ref().map(own_expr),
},
ops: own_region(&for_op.region),
span: for_op.span,
}),
IrOp::OriginalFor(for_op) => Op::OriginalFor(OriginalFor {
node: for_op.id().node().index(),
ops: own_region(&for_op.region),
span: for_op.span,
}),
IrOp::Slot(slot) => Op::Slot(Slot {
name: own_name(&slot.name),
attributes: slot.attributes.iter().map(own_attribute).collect(),
bindings: slot.bindings.iter().map(own_binding).collect(),
fallback: own_region(&slot.fallback),
span: slot.span,
}),
}
}
pub(super) fn own_name(name: &DynamicName<'_>) -> Name {
match name {
DynamicName::Static(text) => Name::Static(String::from(*text)),
DynamicName::Dynamic(expr) => Name::Dynamic(own_expr(expr)),
}
}
pub(super) fn own_attribute(attribute: &IrAttribute<'_>) -> Attribute {
Attribute {
name: String::from(attribute.name),
value: attribute.value.map(String::from),
span: attribute.span,
}
}
fn count_op(op: &Op) -> u64 {
ensure_sufficient_stack(|| count_op_guarded(op))
}
fn count_op_guarded(op: &Op) -> u64 {
match op {
Op::Element(element) => {
1 + element.bindings.len() as u64 + element.children.iter().map(count_op).sum::<u64>()
}
Op::Component(component) => {
1 + component.bindings.len() as u64
+ component.children.iter().map(count_op).sum::<u64>()
}
Op::Text(_) | Op::Interpolation(_) | Op::Comment(_) => 1,
Op::If(if_op) => {
1 + if_op
.branches
.iter()
.flat_map(|branch| branch.ops.iter())
.map(count_op)
.sum::<u64>()
}
Op::For(for_op) => 1 + for_op.ops.iter().map(count_op).sum::<u64>(),
Op::OriginalFor(for_op) => 1 + for_op.ops.iter().map(count_op).sum::<u64>(),
Op::Slot(slot) => {
1 + slot.bindings.len() as u64 + slot.fallback.iter().map(count_op).sum::<u64>()
}
}
}